diff options
author | sam <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
commit | 1bf2fd00c50865c26197a0fb9ce70f417b9fa121 (patch) | |
tree | c6f336fc28b042f00efc2373c71fceadfa394e52 /contrib/hostapd/radius_server.c | |
parent | 620bfba12034be7d2ad4a357063d609ff5b6e63a (diff) | |
download | FreeBSD-src-1bf2fd00c50865c26197a0fb9ce70f417b9fa121.zip FreeBSD-src-1bf2fd00c50865c26197a0fb9ce70f417b9fa121.tar.gz |
Import of hostapd 0.5.8
Diffstat (limited to 'contrib/hostapd/radius_server.c')
-rw-r--r-- | contrib/hostapd/radius_server.c | 368 |
1 files changed, 323 insertions, 45 deletions
diff --git a/contrib/hostapd/radius_server.c b/contrib/hostapd/radius_server.c index bda8bd0..bb78f75 100644 --- a/contrib/hostapd/radius_server.c +++ b/contrib/hostapd/radius_server.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS authentication server - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,13 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <arpa/inet.h> +#include "includes.h" #include <net/if.h> #include "common.h" @@ -37,6 +31,19 @@ static struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; +struct radius_server_counters { + u32 access_requests; + u32 invalid_requests; + u32 dup_access_requests; + u32 access_accepts; + u32 access_rejects; + u32 access_challenges; + u32 malformed_access_requests; + u32 bad_authenticators; + u32 packets_dropped; + u32 unknown_types; +}; + struct radius_session { struct radius_session *next; struct radius_client *client; @@ -47,6 +54,15 @@ struct radius_session { size_t eapKeyDataLen, eapReqDataLen; Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq; Boolean portEnabled, eapTimeout; + + struct radius_msg *last_msg; + char *last_from_addr; + int last_from_port; + struct sockaddr_storage last_from; + socklen_t last_fromlen; + u8 last_identifier; + struct radius_msg *last_reply; + u8 last_authenticator[16]; }; struct radius_client { @@ -60,18 +76,20 @@ struct radius_client { char *shared_secret; int shared_secret_len; struct radius_session *sessions; + struct radius_server_counters counters; }; struct radius_server_data { int auth_sock; struct radius_client *clients; - struct radius_server_session *sessions; unsigned int next_sess_id; void *hostapd_conf; int num_sess; void *eap_sim_db_priv; void *ssl_ctx; int ipv6; + struct os_time start_time; + struct radius_server_counters counters; }; @@ -153,17 +171,31 @@ static void radius_server_session_free(struct radius_server_data *data, free(sess->eapKeyData); free(sess->eapReqData); eap_sm_deinit(sess->eap); + if (sess->last_msg) { + radius_msg_free(sess->last_msg); + free(sess->last_msg); + } + free(sess->last_from_addr); + if (sess->last_reply) { + radius_msg_free(sess->last_reply); + free(sess->last_reply); + } free(sess); data->num_sess--; } +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx); + static void radius_server_session_remove(struct radius_server_data *data, struct radius_session *sess) { struct radius_client *client = sess->client; struct radius_session *session, *prev; + eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); + prev = NULL; session = client->sessions; while (session) { @@ -182,6 +214,16 @@ static void radius_server_session_remove(struct radius_server_data *data, } +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct radius_server_data *data = eloop_ctx; + struct radius_session *sess = timeout_ctx; + RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); +} + + static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) { struct radius_server_data *data = eloop_ctx; @@ -204,11 +246,10 @@ radius_server_new_session(struct radius_server_data *data, return NULL; } - sess = malloc(sizeof(*sess)); - if (sess == NULL) { + sess = wpa_zalloc(sizeof(*sess)); + if (sess == NULL) return NULL; - } - memset(sess, 0, sizeof(*sess)); + sess->server = data; sess->client = client; sess->sess_id = data->next_sess_id++; @@ -303,6 +344,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, msg = radius_msg_new(code, request->hdr->identifier); if (msg == NULL) { + RADIUS_DEBUG("Failed to allocate reply message"); return NULL; } @@ -383,6 +425,8 @@ static int radius_server_reject(struct radius_server_data *data, radius_msg_dump(msg); } + data->counters.access_rejects++; + client->counters.access_rejects++; if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, (struct sockaddr *) from, sizeof(*from)) < 0) { perror("sendto[RADIUS SRV]"); @@ -400,28 +444,31 @@ static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, struct radius_client *client, - const char *from_addr, int from_port) + const char *from_addr, int from_port, + struct radius_session *force_sess) { u8 *eap = NULL; size_t eap_len; - int res, state_included; + int res, state_included = 0; u8 statebuf[4], resp_id; unsigned int state; struct radius_session *sess; struct radius_msg *reply; struct eap_hdr *hdr; - /* TODO: Implement duplicate packet processing */ - - res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, - sizeof(statebuf)); - state_included = res >= 0; - if (res == sizeof(statebuf)) { - state = (statebuf[0] << 24) | (statebuf[1] << 16) | - (statebuf[2] << 8) | statebuf[3]; - sess = radius_server_get_session(client, state); - } else { - sess = NULL; + if (force_sess) + sess = force_sess; + else { + res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, + sizeof(statebuf)); + state_included = res >= 0; + if (res == sizeof(statebuf)) { + state = (statebuf[0] << 24) | (statebuf[1] << 16) | + (statebuf[2] << 8) | statebuf[3]; + sess = radius_server_get_session(client, state); + } else { + sess = NULL; + } } if (sess) { @@ -441,10 +488,35 @@ static int radius_server_request(struct radius_server_data *data, } } + if (sess->last_from_port == from_port && + sess->last_identifier == msg->hdr->identifier && + os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) == + 0) { + RADIUS_DEBUG("Duplicate message from %s", from_addr); + data->counters.dup_access_requests++; + client->counters.dup_access_requests++; + + if (sess->last_reply) { + res = sendto(data->auth_sock, sess->last_reply->buf, + sess->last_reply->buf_used, 0, + (struct sockaddr *) from, fromlen); + if (res < 0) { + perror("sendto[RADIUS SRV]"); + } + return 0; + } + + RADIUS_DEBUG("No previous reply available for duplicate " + "message"); + return -1; + } + eap = radius_msg_get_eap(msg, &eap_len); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); + data->counters.packets_dropped++; + client->counters.packets_dropped++; return -1; } @@ -475,18 +547,31 @@ static int radius_server_request(struct radius_server_data *data, } else if (sess->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set - generate EAP-Failure"); - hdr = malloc(sizeof(*hdr)); + hdr = wpa_zalloc(sizeof(*hdr)); if (hdr) { - memset(hdr, 0, sizeof(*hdr)); hdr->identifier = resp_id; hdr->length = htons(sizeof(*hdr)); sess->eapReqData = (u8 *) hdr; sess->eapReqDataLen = sizeof(*hdr); } + } else if (eap_sm_method_pending(sess->eap)) { + if (sess->last_msg) { + radius_msg_free(sess->last_msg); + free(sess->last_msg); + } + sess->last_msg = msg; + sess->last_from_port = from_port; + free(sess->last_from_addr); + sess->last_from_addr = strdup(from_addr); + sess->last_fromlen = fromlen; + memcpy(&sess->last_from, from, fromlen); + return -2; } else { RADIUS_DEBUG("No EAP data from the state machine - ignore this" " Access-Request silently (assuming it was a " "duplicate)"); + data->counters.packets_dropped++; + client->counters.packets_dropped++; return -1; } @@ -502,18 +587,47 @@ static int radius_server_request(struct radius_server_data *data, radius_msg_dump(reply); } + switch (reply->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + data->counters.access_accepts++; + client->counters.access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + data->counters.access_rejects++; + client->counters.access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + data->counters.access_challenges++; + client->counters.access_challenges++; + break; + } res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, (struct sockaddr *) from, fromlen); if (res < 0) { perror("sendto[RADIUS SRV]"); } - radius_msg_free(reply); - free(reply); + if (sess->last_reply) { + radius_msg_free(sess->last_reply); + free(sess->last_reply); + } + sess->last_reply = reply; + sess->last_from_port = from_port; + sess->last_identifier = msg->hdr->identifier; + os_memcpy(sess->last_authenticator, msg->hdr->authenticator, + 16); + } else { + data->counters.packets_dropped++; + client->counters.packets_dropped++; } if (sess->eapSuccess || sess->eapFail) { - RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); - radius_server_session_remove(data, sess); + RADIUS_DEBUG("Removing completed session 0x%x after timeout", + sess->sess_id); + eloop_cancel_timeout(radius_server_session_remove_timeout, + data, sess); + eloop_register_timeout(10, 0, + radius_server_session_remove_timeout, + data, sess); } return 0; @@ -576,12 +690,15 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (client == NULL) { RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); + data->counters.invalid_requests++; goto fail; } msg = radius_msg_parse(buf, len); if (msg == NULL) { RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); + data->counters.malformed_access_requests++; + client->counters.malformed_access_requests++; goto fail; } @@ -594,17 +711,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); + data->counters.unknown_types++; + client->counters.unknown_types++; goto fail; } + data->counters.access_requests++; + client->counters.access_requests++; + if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, client->shared_secret_len, NULL)) { RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); + data->counters.bad_authenticators++; + client->counters.bad_authenticators++; goto fail; } - radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, - client, abuf, from_port); + if (radius_server_request(data, msg, (struct sockaddr *) &from, + fromlen, client, abuf, from_port, NULL) == + -2) + return; /* msg was stored with the session */ fail: if (msg) { @@ -796,12 +922,11 @@ radius_server_read_clients(const char *client_file, int ipv6) break; } - entry = malloc(sizeof(*entry)); + entry = wpa_zalloc(sizeof(*entry)); if (entry == NULL) { failed = 1; break; } - memset(entry, 0, sizeof(*entry)); entry->shared_secret = strdup(pos); if (entry->shared_secret == NULL) { failed = 1; @@ -864,11 +989,11 @@ radius_server_init(struct radius_server_conf *conf) } #endif /* CONFIG_IPV6 */ - data = malloc(sizeof(*data)); - if (data == NULL) { + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) return NULL; - } - memset(data, 0, sizeof(*data)); + + os_get_time(&data->start_time); data->hostapd_conf = conf->hostapd_conf; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; @@ -924,8 +1049,113 @@ void radius_server_deinit(struct radius_server_data *data) int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen) { - /* TODO: add support for RADIUS authentication server MIB */ - return 0; + int ret, uptime; + unsigned int idx; + char *end, *pos; + struct os_time now; + struct radius_client *cli; + + /* RFC 2619 - RADIUS Authentication Server MIB */ + + if (data == NULL || buflen == 0) + return 0; + + pos = buf; + end = buf + buflen; + + os_get_time(&now); + uptime = (now.sec - data->start_time.sec) * 100 + + ((now.usec - data->start_time.usec) / 10000) % 100; + ret = snprintf(pos, end - pos, + "RADIUS-AUTH-SERVER-MIB\n" + "radiusAuthServIdent=hostapd\n" + "radiusAuthServUpTime=%d\n" + "radiusAuthServResetTime=0\n" + "radiusAuthServConfigReset=4\n", + uptime); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + + ret = snprintf(pos, end - pos, + "radiusAuthServTotalAccessRequests=%u\n" + "radiusAuthServTotalInvalidRequests=%u\n" + "radiusAuthServTotalDupAccessRequests=%u\n" + "radiusAuthServTotalAccessAccepts=%u\n" + "radiusAuthServTotalAccessRejects=%u\n" + "radiusAuthServTotalAccessChallenges=%u\n" + "radiusAuthServTotalMalformedAccessRequests=%u\n" + "radiusAuthServTotalBadAuthenticators=%u\n" + "radiusAuthServTotalPacketsDropped=%u\n" + "radiusAuthServTotalUnknownTypes=%u\n", + data->counters.access_requests, + data->counters.invalid_requests, + data->counters.dup_access_requests, + data->counters.access_accepts, + data->counters.access_rejects, + data->counters.access_challenges, + data->counters.malformed_access_requests, + data->counters.bad_authenticators, + data->counters.packets_dropped, + data->counters.unknown_types); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + + for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { + char abuf[50], mbuf[50]; +#ifdef CONFIG_IPV6 + if (data->ipv6) { + if (inet_ntop(AF_INET6, &cli->addr6, abuf, + sizeof(abuf)) == NULL) + abuf[0] = '\0'; + if (inet_ntop(AF_INET6, &cli->mask6, abuf, + sizeof(mbuf)) == NULL) + mbuf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + if (!data->ipv6) { + snprintf(abuf, sizeof(abuf), "%s", + inet_ntoa(cli->addr)); + snprintf(mbuf, sizeof(mbuf), "%s", + inet_ntoa(cli->mask)); + } + + ret = snprintf(pos, end - pos, + "radiusAuthClientIndex=%u\n" + "radiusAuthClientAddress=%s/%s\n" + "radiusAuthServAccessRequests=%u\n" + "radiusAuthServDupAccessRequests=%u\n" + "radiusAuthServAccessAccepts=%u\n" + "radiusAuthServAccessRejects=%u\n" + "radiusAuthServAccessChallenges=%u\n" + "radiusAuthServMalformedAccessRequests=%u\n" + "radiusAuthServBadAuthenticators=%u\n" + "radiusAuthServPacketsDropped=%u\n" + "radiusAuthServUnknownTypes=%u\n", + idx, + abuf, mbuf, + cli->counters.access_requests, + cli->counters.dup_access_requests, + cli->counters.access_accepts, + cli->counters.access_rejects, + cli->counters.access_challenges, + cli->counters.malformed_access_requests, + cli->counters.bad_authenticators, + cli->counters.packets_dropped, + cli->counters.unknown_types); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + } + + return pos - buf; } @@ -1039,6 +1269,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, { struct radius_session *sess = ctx; const struct hostapd_eap_user *eap_user; + int i, count; eap_user = hostapd_get_eap_user(sess->server->hostapd_conf, identity, identity_len, phase2); @@ -1046,9 +1277,13 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, return -1; memset(user, 0, sizeof(*user)); - memcpy(user->methods, eap_user->methods, - EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? - EAP_USER_MAX_METHODS : EAP_MAX_METHODS); + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } if (eap_user->password) { user->password = malloc(eap_user->password_len); @@ -1057,6 +1292,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; @@ -1072,3 +1308,45 @@ static struct eapol_callbacks radius_server_eapol_cb = .set_eapKeyData = radius_server_set_eapKeyData, .get_eap_user = radius_server_get_eap_user, }; + + +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) +{ + struct radius_client *cli; + struct radius_session *s, *sess = NULL; + struct radius_msg *msg; + + if (data == NULL) + return; + + for (cli = data->clients; cli; cli = cli->next) { + for (s = cli->sessions; s; s = s->next) { + if (s->eap == ctx && s->last_msg) { + sess = s; + break; + } + if (sess) + break; + } + if (sess) + break; + } + + if (sess == NULL) { + RADIUS_DEBUG("No session matched callback ctx"); + return; + } + + msg = sess->last_msg; + sess->last_msg = NULL; + eap_sm_pending_cb(sess->eap); + if (radius_server_request(data, msg, + (struct sockaddr *) &sess->last_from, + sess->last_fromlen, cli, + sess->last_from_addr, + sess->last_from_port, sess) == -2) + return; /* msg was stored with the session */ + + radius_msg_free(msg); + free(msg); +} |