📄 radius_server.c
字号:
/* * hostapd / RADIUS authentication server * Copyright (c) 2005-2008, 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 * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include "includes.h"#include <net/if.h>#include "common.h"#include "radius.h"#include "eloop.h"#include "defs.h"#include "eap_server/eap.h"#include "radius_server.h"#define RADIUS_SESSION_TIMEOUT 60#define RADIUS_MAX_SESSION 100#define RADIUS_MAX_MSG_LEN 3000static 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; struct radius_server_data *server; unsigned int sess_id; struct eap_sm *eap; struct eap_eapol_interface *eap_if; 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 { struct radius_client *next; struct in_addr addr; struct in_addr mask;#ifdef CONFIG_IPV6 struct in6_addr addr6; struct in6_addr mask6;#endif /* CONFIG_IPV6 */ 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; unsigned int next_sess_id; void *conf_ctx; int num_sess; void *eap_sim_db_priv; void *ssl_ctx; u8 *pac_opaque_encr_key; char *eap_fast_a_id; int eap_sim_aka_result_ind; int tnc; int ipv6; struct os_time start_time; struct radius_server_counters counters; int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user);};extern int wpa_debug_level;#define RADIUS_DEBUG(args...) \wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)#define RADIUS_ERROR(args...) \wpa_printf(MSG_ERROR, "RADIUS SRV: " args)#define RADIUS_DUMP(args...) \wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)#define RADIUS_DUMP_ASCII(args...) \wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);static struct radius_client *radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, int ipv6){ struct radius_client *client = data->clients; while (client) {#ifdef CONFIG_IPV6 if (ipv6) { struct in6_addr *addr6; int i; addr6 = (struct in6_addr *) addr; for (i = 0; i < 16; i++) { if ((addr6->s6_addr[i] & client->mask6.s6_addr[i]) != (client->addr6.s6_addr[i] & client->mask6.s6_addr[i])) { i = 17; break; } } if (i == 16) { break; } }#endif /* CONFIG_IPV6 */ if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == (addr->s_addr & client->mask.s_addr)) { break; } client = client->next; } return client;}static struct radius_session *radius_server_get_session(struct radius_client *client, unsigned int sess_id){ struct radius_session *sess = client->sessions; while (sess) { if (sess->sess_id == sess_id) { break; } sess = sess->next; } return sess;}static void radius_server_session_free(struct radius_server_data *data, struct radius_session *sess){ eloop_cancel_timeout(radius_server_session_timeout, data, sess); eap_server_sm_deinit(sess->eap); if (sess->last_msg) { radius_msg_free(sess->last_msg); os_free(sess->last_msg); } os_free(sess->last_from_addr); if (sess->last_reply) { radius_msg_free(sess->last_reply); os_free(sess->last_reply); } os_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) { if (session == sess) { if (prev == NULL) { client->sessions = sess->next; } else { prev->next = sess->next; } radius_server_session_free(data, sess); break; } prev = session; session = session->next; }}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; struct radius_session *sess = timeout_ctx; RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); radius_server_session_remove(data, sess);}static struct radius_session *radius_server_new_session(struct radius_server_data *data, struct radius_client *client){ struct radius_session *sess; if (data->num_sess >= RADIUS_MAX_SESSION) { RADIUS_DEBUG("Maximum number of existing session - no room " "for a new session"); return NULL; } sess = os_zalloc(sizeof(*sess)); if (sess == NULL) return NULL; sess->server = data; sess->client = client; sess->sess_id = data->next_sess_id++; sess->next = client->sessions; client->sessions = sess; eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, radius_server_session_timeout, data, sess); data->num_sess++; return sess;}static struct radius_session *radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, struct radius_msg *msg){ u8 *user; size_t user_len; int res; struct radius_session *sess; struct eap_config eap_conf; RADIUS_DEBUG("Creating a new session"); user = os_malloc(256); if (user == NULL) { return NULL; } res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); if (res < 0 || res > 256) { RADIUS_DEBUG("Could not get User-Name"); os_free(user); return NULL; } user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); os_free(user); if (res == 0) { RADIUS_DEBUG("Matching user entry found"); sess = radius_server_new_session(data, client); if (sess == NULL) { RADIUS_DEBUG("Failed to create a new session"); return NULL; } } else { RADIUS_DEBUG("User-Name not found from user database"); return NULL; } os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; eap_conf.backend_auth = TRUE; eap_conf.eap_server = 1; eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key; eap_conf.eap_fast_a_id = data->eap_fast_a_id; eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { RADIUS_DEBUG("Failed to initialize EAP state machine for the " "new session"); radius_server_session_free(data, sess); return NULL; } sess->eap_if = eap_get_interface(sess->eap); sess->eap_if->eapRestart = TRUE; sess->eap_if->portEnabled = TRUE; RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); return sess;}static struct radius_msg *radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, struct radius_session *sess, struct radius_msg *request){ struct radius_msg *msg; int code; unsigned int sess_id; if (sess->eap_if->eapFail) { sess->eap_if->eapFail = FALSE; code = RADIUS_CODE_ACCESS_REJECT; } else if (sess->eap_if->eapSuccess) { sess->eap_if->eapSuccess = FALSE; code = RADIUS_CODE_ACCESS_ACCEPT; } else { sess->eap_if->eapReq = FALSE; code = RADIUS_CODE_ACCESS_CHALLENGE; } msg = radius_msg_new(code, request->hdr->identifier); if (msg == NULL) { RADIUS_DEBUG("Failed to allocate reply message"); return NULL; } sess_id = htonl(sess->sess_id); if (code == RADIUS_CODE_ACCESS_CHALLENGE && !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, (u8 *) &sess_id, sizeof(sess_id))) { RADIUS_DEBUG("Failed to add State attribute"); } if (sess->eap_if->eapReqData && !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), wpabuf_len(sess->eap_if->eapReqData))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); } if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { len = sess->eap_if->eapKeyDataLen / 2; } if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, (u8 *) client->shared_secret, client->shared_secret_len, sess->eap_if->eapKeyData + len, len, sess->eap_if->eapKeyData, len)) { RADIUS_DEBUG("Failed to add MPPE key attributes"); } } if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); os_free(msg); return NULL; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, request->hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } return msg;}static int radius_server_reject(struct radius_server_data *data, struct radius_client *client, struct radius_msg *request, struct sockaddr *from, socklen_t fromlen, const char *from_addr, int from_port){ struct radius_msg *msg; int ret = 0; struct eap_hdr eapfail; RADIUS_DEBUG("Reject invalid request from %s:%d", from_addr, from_port); msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, request->hdr->identifier); if (msg == NULL) { return -1; } os_memset(&eapfail, 0, sizeof(eapfail)); eapfail.code = EAP_CODE_FAILURE; eapfail.identifier = 0; eapfail.length = host_to_be16(sizeof(eapfail)); if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { RADIUS_DEBUG("Failed to add EAP-Message attribute"); } if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); os_free(msg); return -1; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, request->hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } if (wpa_debug_level <= MSG_MSGDUMP) { 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]"); ret = -1; } radius_msg_free(msg); os_free(msg); return ret;}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, struct radius_session *force_sess){ u8 *eap = NULL; size_t eap_len; int res, state_included = 0; u8 statebuf[4]; unsigned int state; struct radius_session *sess; struct radius_msg *reply; 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 = WPA_GET_BE32(statebuf); sess = radius_server_get_session(client, state); } else { sess = NULL; } } if (sess) { RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); } else if (state_included) { RADIUS_DEBUG("State attribute included but no session found"); radius_server_reject(data, client, msg, from, fromlen, from_addr, from_port); return -1; } else { sess = radius_server_get_new_session(data, client, msg); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); radius_server_reject(data, client, msg, from, fromlen, from_addr, from_port); return -1; } } 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; } RADIUS_DUMP("Received EAP data", eap, eap_len); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. * Include EAP-Response/Nak with no preferred method if * code == request. * If code is not 1-4, discard the packet silently. * Or is this already done by the EAP state machine? */ wpabuf_free(sess->eap_if->eapRespData); sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); if (sess->eap_if->eapRespData == NULL) os_free(eap); eap = NULL; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || sess->eap_if->eapFail) && sess->eap_if->eapReqData) { RADIUS_DUMP("EAP data from the state machine", wpabuf_head(sess->eap_if->eapReqData), wpabuf_len(sess->eap_if->eapReqData)); } else if (sess->eap_if->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set"); } else if (eap_sm_method_pending(sess->eap)) { if (sess->last_msg) { radius_msg_free(sess->last_msg); os_free(sess->last_msg); } sess->last_msg = msg; sess->last_from_port = from_port; os_free(sess->last_from_addr); sess->last_from_addr = os_strdup(from_addr); sess->last_fromlen = fromlen; os_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; } reply = radius_server_encapsulate_eap(data, client, sess, msg); if (reply) { RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { 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]"); } if (sess->last_reply) { radius_msg_free(sess->last_reply); os_free(sess->last_reply);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -