aboutsummaryrefslogtreecommitdiff
path: root/contrib/hostapd/radius_server.c
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2007-07-09 16:15:06 +0000
committerSam Leffler <sam@FreeBSD.org>2007-07-09 16:15:06 +0000
commit75cbf10273fddabf1de109ac241e64defd66eb38 (patch)
treec6f336fc28b042f00efc2373c71fceadfa394e52 /contrib/hostapd/radius_server.c
parentb449aee71d92a35e5293f3df4f0a564f77fbd02b (diff)
downloadsrc-75cbf10273fddabf1de109ac241e64defd66eb38.tar.gz
src-75cbf10273fddabf1de109ac241e64defd66eb38.zip
Import of hostapd 0.5.8
Notes
Notes: svn path=/vendor/hostapd/dist/; revision=171322
Diffstat (limited to 'contrib/hostapd/radius_server.c')
-rw-r--r--contrib/hostapd/radius_server.c368
1 files changed, 323 insertions, 45 deletions
diff --git a/contrib/hostapd/radius_server.c b/contrib/hostapd/radius_server.c
index bda8bd0a4d83..bb78f751640c 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);
+}