/* Process the RADIUS frames from Authentication Server */ static RadiusRxResult ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len, void *data) { /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be * present when packet contains an EAP-Message attribute */ if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, 0) < 0 && radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { wpa_printf(MSG_DEBUG, "Allowing RADIUS " "Access-Reject without Message-Authenticator " "since it does not include EAP-Message\n"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req)) { printf("Incoming RADIUS packet did not have correct " "Message-Authenticator - dropped\n"); return RADIUS_RX_UNKNOWN; } if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { printf("Unknown RADIUS message code\n"); return RADIUS_RX_UNKNOWN; } wpa_s->radius_identifier = -1; wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); if (wpa_s->last_recv_radius) { radius_msg_free(wpa_s->last_recv_radius); free(wpa_s->last_recv_radius); } wpa_s->last_recv_radius = msg; switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: wpa_s->radius_access_accept_received = 1; ieee802_1x_get_keys(wpa_s, msg, req, shared_secret, shared_secret_len); break; case RADIUS_CODE_ACCESS_REJECT: wpa_s->radius_access_reject_received = 1; break; } ieee802_1x_decapsulate_radius(wpa_s); if ((msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && eapol_test_num_reauths < 0) || msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) { eloop_terminate(); } return RADIUS_RX_QUEUED; }
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; const struct hostapd_eap_user *eap_user; int res; struct radius_session *sess; struct eap_config eap_conf; RADIUS_DEBUG("Creating a new session"); user = VM_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"); VM_FREE(user); return NULL; } user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); eap_user = hostapd_get_eap_user(data->hostapd_conf, user, user_len, 0); VM_FREE(user); if (eap_user) { 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; } 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; sess->eap = eap_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->eapRestart = TRUE; sess->portEnabled = TRUE; RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); return sess; }
/* Process the RADIUS frames from Authentication Server */ static RadiusRxResult ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, struct radius_msg *msg, struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len, void *data) { #if 0 u32 session_timeout, termination_action; int session_timeout_set; int acct_interim_interval; #endif #if 0 sta = ap_get_sta_radius_identifier(hapd, msg->hdr->identifier); if (sta == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not " "find matching station for this RADIUS " "message\n"); return RADIUS_RX_UNKNOWN; } #endif /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be * present when packet contains an EAP-Message attribute */ if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, 0) < 0 && radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { wpa_printf(MSG_DEBUG, "Allowing RADIUS " "Access-Reject without Message-Authenticator " "since it does not include EAP-Message\n"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req)) { printf("Incoming RADIUS packet did not have correct " "Message-Authenticator - dropped\n"); return RADIUS_RX_UNKNOWN; } if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { printf("Unknown RADIUS message code\n"); return RADIUS_RX_UNKNOWN; } wpa_s->radius_identifier = -1; wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); if (wpa_s->last_recv_radius) { radius_msg_free(wpa_s->last_recv_radius); free(wpa_s->last_recv_radius); } wpa_s->last_recv_radius = msg; #if 0 session_timeout_set = !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &session_timeout); if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, &termination_action)) termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; if (hapd->conf->radius_acct_interim_interval == 0 && msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, &acct_interim_interval) == 0) { if (acct_interim_interval < 60) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, "ignored too small " "Acct-Interim-Interval %d", acct_interim_interval); } else sta->acct_interim_interval = acct_interim_interval; } switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: /* draft-congdon-radius-8021x-22.txt, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { sta->eapol_sm->reauth_timer.reAuthPeriod = session_timeout; } else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); sta->eapol_sm->be_auth.aSuccess = TRUE; ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, shared_secret_len); if (sta->eapol_sm->keyAvailable) { pmksa_cache_add(hapd, sta, sta->eapol_key_crypt, session_timeout_set ? session_timeout : -1); } break; case RADIUS_CODE_ACCESS_REJECT: sta->eapol_sm->be_auth.aFail = TRUE; break; case RADIUS_CODE_ACCESS_CHALLENGE: if (session_timeout_set) { /* RFC 2869, Ch. 2.3.2 * draft-congdon-radius-8021x-22.txt, Ch. 3.17 */ sta->eapol_sm->be_auth.suppTimeout = session_timeout; } sta->eapol_sm->be_auth.aReq = TRUE; break; } #else switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: wpa_s->radius_access_accept_received = 1; ieee802_1x_get_keys(wpa_s, msg, req, shared_secret, shared_secret_len); break; case RADIUS_CODE_ACCESS_REJECT: wpa_s->radius_access_reject_received = 1; break; } #endif ieee802_1x_decapsulate_radius(wpa_s); /* eapol_sm_step(sta->eapol_sm); */ if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT || msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) { eloop_terminate(); } return RADIUS_RX_QUEUED; }
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; int is_complete = 0; 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; } if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 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); } 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 (is_complete) { 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; }
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_fast_a_id_len = data->eap_fast_a_id_len; eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info; eap_conf.eap_fast_prov = data->eap_fast_prov; eap_conf.pac_key_lifetime = data->pac_key_lifetime; eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; 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 void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_das_data *das = eloop_ctx; u8 buf[1500]; union { struct sockaddr_storage ss; struct sockaddr_in sin; #ifdef CONFIG_IPV6 struct sockaddr_in6 sin6; #endif /* CONFIG_IPV6 */ } from; char abuf[50]; int from_port = 0; socklen_t fromlen; int len; struct radius_msg *msg, *reply = NULL; struct radius_hdr *hdr; struct wpabuf *rbuf; u32 val; int res; struct os_time now; fromlen = sizeof(from); len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); return; } os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); from_port = ntohs(from.sin.sin_port); wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", len, abuf, from_port); if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " "from %s:%d failed", abuf, from_port); return; } if (wpa_debug_level <= MSG_MSGDUMP) radius_msg_dump(msg); if (radius_msg_verify_das_req(msg, das->shared_secret, das->shared_secret_len)) { wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " "from %s:%d - drop", abuf, from_port); goto fail; } os_get_time(&now); res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); if ((unsigned int) abs((int) (now.sec - timestamp)) > das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " "packet from %s:%d - drop", timestamp, (unsigned int) now.sec, abuf, from_port); goto fail; } } else if (das->require_event_timestamp) { wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " "from %s:%d - drop", abuf, from_port); goto fail; } hdr = radius_msg_get_hdr(msg); switch (hdr->code) { case RADIUS_CODE_DISCONNECT_REQUEST: reply = radius_das_disconnect(das, msg, abuf, from_port); break; case RADIUS_CODE_COA_REQUEST: /* TODO */ reply = radius_msg_new(RADIUS_CODE_COA_NAK, hdr->identifier); if (reply == NULL) break; /* Unsupported Service */ if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405)) { radius_msg_free(reply); reply = NULL; break; } break; default: wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " "packet from %s:%d", hdr->code, abuf, from_port); } if (reply) { wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_EVENT_TIMESTAMP, now.sec)) { wpa_printf(MSG_DEBUG, "DAS: Failed to add " "Event-Timestamp attribute"); } if (radius_msg_finish_das_resp(reply, das->shared_secret, das->shared_secret_len, hdr) < 0) { wpa_printf(MSG_DEBUG, "DAS: Failed to add " "Message-Authenticator attribute"); } if (wpa_debug_level <= MSG_MSGDUMP) radius_msg_dump(reply); rbuf = radius_msg_get_buf(reply); res = sendto(das->sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0, (struct sockaddr *) &from.ss, fromlen); if (res < 0) { wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", abuf, from_port, strerror(errno)); } } fail: radius_msg_free(msg); radius_msg_free(reply); }
static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr_in *from, struct radius_client *client) { u8 *eap = NULL; size_t eap_len; int res, state_included; 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 (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); return -1; } else { sess = radius_server_get_new_session(data, client, msg); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); return -1; } } eap = radius_msg_get_eap(msg, &eap_len); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", inet_ntoa(from->sin_addr)); return -1; } RADIUS_DUMP("Received EAP data", eap, eap_len); if (eap_len >= sizeof(*hdr)) { hdr = (struct eap_hdr *) eap; resp_id = hdr->identifier; } else { resp_id = 0; } eap_set_eapRespData(sess->eap, eap, eap_len); free(eap); eap = NULL; sess->eapResp = TRUE; eap_sm_step(sess->eap); if (sess->eapReqData) { RADIUS_DUMP("EAP data from the state machine", sess->eapReqData, sess->eapReqDataLen); } else if (sess->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set - generate EAP-Failure"); hdr = malloc(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 { RADIUS_DEBUG("No EAP data from the state machine - ignore this" " Access-Request silently (assuming it was a " "duplicate)"); return -1; } reply = radius_server_encapsulate_eap(data, client, sess, msg); free(sess->eapReqData); sess->eapReqData = NULL; sess->eapReqDataLen = 0; if (reply) { RADIUS_DEBUG("Reply to %s:%d", inet_ntoa(from->sin_addr), ntohs(from->sin_port)); if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(reply); } res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, (struct sockaddr *) from, sizeof(*from)); if (res < 0) { perror("sendto[RADIUS SRV]"); } radius_msg_free(reply); free(reply); } if (sess->eapSuccess || sess->eapFail) { RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); radius_server_session_remove(data, sess); } return 0; }