static void radius_server_receive_auth(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_server_data *data = eloop_ctx; u8 *buf = NULL; union { struct sockaddr_storage ss; struct sockaddr_in sin; #ifdef CONFIG_IPV6 struct sockaddr_in6 sin6; #endif /* CONFIG_IPV6 */ } from; socklen_t fromlen; int len; struct radius_client *client = NULL; struct radius_msg *msg = NULL; char abuf[50]; int from_port = 0; buf = os_malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { goto fail; } fromlen = sizeof(from); len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { perror("recvfrom[radius_server]"); goto fail; } #ifdef CONFIG_IPV6 if (data->ipv6) { if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, sizeof(abuf)) == NULL) abuf[0] = '\0'; from_port = ntohs(from.sin6.sin6_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); client = radius_server_get_client(data, (struct in_addr *) &from.sin6.sin6_addr, 1); } #endif /* CONFIG_IPV6 */ if (!data->ipv6) { os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); from_port = ntohs(from.sin.sin_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); client = radius_server_get_client(data, &from.sin.sin_addr, 0); } RADIUS_DUMP("Received data", buf, len); 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; } os_free(buf); buf = NULL; if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(msg); } 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; } 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) { radius_msg_free(msg); os_free(msg); } os_free(buf); }
static void radius_server_receive_auth(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_server_data *data = eloop_ctx; u8 *buf = NULL; struct sockaddr_in from; socklen_t fromlen; int len; struct radius_client *client; struct radius_msg *msg = NULL; buf = malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { goto fail; } fromlen = sizeof(from); len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { perror("recvfrom[radius_server]"); goto fail; } RADIUS_DEBUG("Received %d bytes from %s:%d", len, inet_ntoa(from.sin_addr), ntohs(from.sin_port)); RADIUS_DUMP("Received data", buf, len); client = radius_server_get_client(data, &from.sin_addr); if (client == NULL) { RADIUS_DEBUG("Unknown client %s - packet ignored", inet_ntoa(from.sin_addr)); goto fail; } msg = radius_msg_parse(buf, len); if (msg == NULL) { RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); goto fail; } free(buf); buf = NULL; if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(msg); } if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); goto fail; } if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, client->shared_secret_len, NULL)) { RADIUS_DEBUG("Invalid Message-Authenticator from %s", inet_ntoa(from.sin_addr)); goto fail; } radius_server_request(data, msg, &from, client); fail: if (msg) { radius_msg_free(msg); free(msg); } free(buf); }
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 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; }