diff options
-rw-r--r-- | contrib/hostapd/eap.c | 518 | ||||
-rw-r--r-- | contrib/hostapd/eap.h | 38 | ||||
-rw-r--r-- | contrib/hostapd/eapol_sm.c | 457 | ||||
-rw-r--r-- | contrib/hostapd/ieee802_1x.c | 722 | ||||
-rw-r--r-- | contrib/hostapd/ieee802_1x.h | 60 | ||||
-rw-r--r-- | contrib/hostapd/wpa.c | 3558 |
6 files changed, 3346 insertions, 2007 deletions
diff --git a/contrib/hostapd/eap.c b/contrib/hostapd/eap.c index b321ea6..3f623ca 100644 --- a/contrib/hostapd/eap.c +++ b/contrib/hostapd/eap.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP Standalone Authenticator state machine - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / EAP Standalone Authenticator state machine (RFC 4137) + * Copyright (c) 2004-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 @@ -14,103 +14,22 @@ * $FreeBSD$ */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/socket.h> +#include "includes.h" #include "hostapd.h" -#include "eloop.h" #include "sta_info.h" #include "eap_i.h" +#include "state_machine.h" -#define EAP_MAX_AUTH_ROUNDS 50 +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" -extern const struct eap_method eap_method_identity; -#ifdef EAP_MD5 -extern const struct eap_method eap_method_md5; -#endif /* EAP_MD5 */ -#ifdef EAP_TLS -extern const struct eap_method eap_method_tls; -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 -extern const struct eap_method eap_method_mschapv2; -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP -extern const struct eap_method eap_method_peap; -#endif /* EAP_PEAP */ -#ifdef EAP_TLV -extern const struct eap_method eap_method_tlv; -#endif /* EAP_TLV */ -#ifdef EAP_GTC -extern const struct eap_method eap_method_gtc; -#endif /* EAP_GTC */ -#ifdef EAP_TTLS -extern const struct eap_method eap_method_ttls; -#endif /* EAP_TTLS */ -#ifdef EAP_SIM -extern const struct eap_method eap_method_sim; -#endif /* EAP_SIM */ -#ifdef EAP_PAX -extern const struct eap_method eap_method_pax; -#endif /* EAP_PAX */ -#ifdef EAP_PSK -extern const struct eap_method eap_method_psk; -#endif /* EAP_PSK */ - -static const struct eap_method *eap_methods[] = -{ - &eap_method_identity, -#ifdef EAP_MD5 - &eap_method_md5, -#endif /* EAP_MD5 */ -#ifdef EAP_TLS - &eap_method_tls, -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 - &eap_method_mschapv2, -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP - &eap_method_peap, -#endif /* EAP_PEAP */ -#ifdef EAP_TTLS - &eap_method_ttls, -#endif /* EAP_TTLS */ -#ifdef EAP_TLV - &eap_method_tlv, -#endif /* EAP_TLV */ -#ifdef EAP_GTC - &eap_method_gtc, -#endif /* EAP_GTC */ -#ifdef EAP_SIM - &eap_method_sim, -#endif /* EAP_SIM */ -#ifdef EAP_PAX - &eap_method_pax, -#endif /* EAP_PAX */ -#ifdef EAP_PSK - &eap_method_psk, -#endif /* EAP_PSK */ -}; -#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) - - -const struct eap_method * eap_sm_get_eap_methods(int method) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (eap_methods[i]->method == method) - return eap_methods[i]; - } - return NULL; -} +#define EAP_MAX_AUTH_ROUNDS 50 static void eap_user_free(struct eap_user *user); -/* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */ +/* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, @@ -120,34 +39,11 @@ static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len); static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len); static int eap_sm_nextId(struct eap_sm *sm, int id); static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len); -static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm); +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor); static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ - int global) - -#define SM_ENTRY(machine, state) \ -if (!global || sm->machine ## _state != machine ## _ ## state) { \ - sm->changed = TRUE; \ - wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ -} \ -sm->machine ## _state = machine ## _ ## state; - -#define SM_ENTER(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 0) -#define SM_ENTER_GLOBAL(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 1) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eap_sm *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) { return sm->eapol_cb->get_bool(sm->eapol_ctx, var); @@ -179,6 +75,19 @@ static void eapol_set_eapKeyData(struct eap_sm *sm, } +/** + * eap_user_get - Fetch user information from the database + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @identity: Identity (User-Name) of the user + * @identity_len: Length of identity in bytes + * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user + * Returns: 0 on success, or -1 on failure + * + * This function is used to fetch user information for EAP. The user will be + * selected based on the specified identity. sm->user and + * sm->user_eap_method_index are updated for the new user when a matching user + * is found. sm->user can be used to get user information (e.g., password). + */ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2) { @@ -191,10 +100,9 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, eap_user_free(sm->user); sm->user = NULL; - user = malloc(sizeof(*user)); + user = wpa_zalloc(sizeof(*user)); if (user == NULL) return -1; - memset(user, 0, sizeof(*user)); if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, identity_len, phase2, user) != 0) { @@ -230,9 +138,11 @@ SM_STATE(EAP, INITIALIZE) /* eapKeyAvailable = FALSE */ eapol_set_bool(sm, EAPOL_eapRestart, FALSE); - /* This is not defined in draft-ietf-eap-statemachine-05.txt, but - * method state needs to be reseted here so that it does not remain in - * success state when re-authentication starts. */ + /* + * This is not defined in RFC 4137, but method state needs to be + * reseted here so that it does not remain in success state when + * re-authentication starts. + */ if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; @@ -249,6 +159,7 @@ SM_STATE(EAP, INITIALIZE) } } sm->num_rounds = 0; + sm->method_pending = METHOD_PENDING_NONE; } @@ -262,7 +173,8 @@ SM_STATE(EAP, PICK_UP_METHOD) sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } - sm->m = eap_sm_get_eap_methods(sm->currentMethod); + sm->m = eap_sm_get_eap_methods(EAP_VENDOR_IETF, + sm->currentMethod); if (sm->m && sm->m->initPickUp) { sm->eap_method_priv = sm->m->initPickUp(sm); if (sm->eap_method_priv == NULL) { @@ -399,14 +311,21 @@ SM_STATE(EAP, METHOD_RESPONSE) SM_STATE(EAP, PROPOSE_METHOD) { + int vendor; + EapType type; + SM_ENTRY(EAP, PROPOSE_METHOD); - sm->currentMethod = eap_sm_Policy_getNextMethod(sm); + type = eap_sm_Policy_getNextMethod(sm, &vendor); + if (vendor == EAP_VENDOR_IETF) + sm->currentMethod = type; + else + sm->currentMethod = EAP_TYPE_EXPANDED; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } - sm->m = eap_sm_get_eap_methods(sm->currentMethod); + sm->m = eap_sm_get_eap_methods(vendor, type); if (sm->m) { sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { @@ -536,7 +455,9 @@ SM_STEP(EAP) SM_ENTER(EAP, SELECT_ACTION); else if (sm->rxResp && (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK)) + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK))) SM_ENTER(EAP, NAK); else SM_ENTER(EAP, PICK_UP_METHOD); @@ -570,14 +491,25 @@ SM_STEP(EAP) case EAP_RECEIVED: if (sm->rxResp && (sm->respId == sm->currentId) && (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK) + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK)) && (sm->methodState == METHOD_PROPOSED)) SM_ENTER(EAP, NAK); else if (sm->rxResp && (sm->respId == sm->currentId) && - (sm->respMethod == sm->currentMethod)) + ((sm->respMethod == sm->currentMethod) || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); - else + else { + wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " + "rxResp=%d respId=%d currentId=%d " + "respMethod=%d currentMethod=%d", + sm->rxResp, sm->respId, sm->currentId, + sm->respMethod, sm->currentMethod); SM_ENTER(EAP, DISCARD); + } break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); @@ -595,13 +527,48 @@ SM_STEP(EAP) SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transits to SELECT_ACTION and + * METHOD_REQUEST from this METHOD_RESPONSE state. + */ if (sm->methodState == METHOD_END) SM_ENTER(EAP, SELECT_ACTION); - else + else if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, METHOD_RESPONSE); + } else SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_PROPOSE_METHOD: - SM_ENTER(EAP, METHOD_REQUEST); + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transit to METHOD_REQUEST from this + * PROPOSE_METHOD state. + */ + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + if (sm->user_eap_method_index > 0) + sm->user_eap_method_index--; + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, PROPOSE_METHOD); + } else + SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_NAK: SM_ENTER(EAP, SELECT_ACTION); @@ -644,6 +611,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) sm->rxResp = FALSE; sm->respId = -1; sm->respMethod = EAP_TYPE_NONE; + sm->respVendor = EAP_VENDOR_IETF; + sm->respVendorMethod = EAP_TYPE_NONE; if (resp == NULL || len < sizeof(*hdr)) return; @@ -662,11 +631,26 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) if (hdr->code == EAP_CODE_RESPONSE) sm->rxResp = TRUE; - if (len > sizeof(*hdr)) - sm->respMethod = *((u8 *) (hdr + 1)); + if (plen > sizeof(*hdr)) { + u8 *pos = (u8 *) (hdr + 1); + sm->respMethod = *pos++; + if (sm->respMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->respVendor = WPA_GET_BE24(pos); + pos += 3; + sm->respVendorMethod = WPA_GET_BE32(pos); + } + } wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " - "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod); + "respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, + sm->respVendorMethod); } @@ -707,7 +691,7 @@ static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len) static int eap_sm_nextId(struct eap_sm *sm, int id) { if (id < 0) { - /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a + /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a * random number */ id = rand() & 0xff; if (id != sm->lastId) @@ -717,22 +701,40 @@ static int eap_sm_nextId(struct eap_sm *sm, int id) } +/** + * eap_sm_process_nak - Process EAP-Response/Nak + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @nak_list: Nak list (allowed methods) from the supplicant + * @len: Length of nak_list in bytes + * + * This function is called when EAP-Response/Nak is received from the + * supplicant. This can happen for both phase 1 and phase 2 authentications. + */ void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) { - int i, j; + int i; + size_t j; + + if (sm->user == NULL) + return; wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " "index %d)", sm->user_eap_method_index); wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", - sm->user->methods, EAP_MAX_METHODS); + (u8 *) sm->user->methods, + EAP_MAX_METHODS * sizeof(sm->user->methods[0])); wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", nak_list, len); i = sm->user_eap_method_index; - while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) { + while (i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) + goto not_found; for (j = 0; j < len; j++) { - if (nak_list[j] == sm->user->methods[i]) { + if (nak_list[j] == sm->user->methods[i].method) { break; } } @@ -743,14 +745,19 @@ void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) continue; } + not_found: /* not found - remove from the list */ memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - EAP_MAX_METHODS - i - 1); - sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE; + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); + sm->user->methods[EAP_MAX_METHODS - 1].vendor = + EAP_VENDOR_IETF; + sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; } wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", - sm->user->methods, EAP_MAX_METHODS); + (u8 *) sm->user->methods, EAP_MAX_METHODS * + sizeof(sm->user->methods[0])); } @@ -770,9 +777,10 @@ static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len) } -static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) { EapType next; + int idx = sm->user_eap_method_index; /* In theory, there should be no problems with starting * re-authentication with something else than EAP-Request/Identity and @@ -782,16 +790,21 @@ static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) * Re-auth sets currentId == -1, so that can be used here to select * whether Identity needs to be requested again. */ if (sm->identity == NULL || sm->currentId == -1) { + *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_IDENTITY; sm->update_user = TRUE; - } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != - EAP_TYPE_NONE) { - next = sm->user->methods[sm->user_eap_method_index++]; + } else if (sm->user && idx < EAP_MAX_METHODS && + (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || + sm->user->methods[idx].method != EAP_TYPE_NONE)) { + *vendor = sm->user->methods[idx].vendor; + next = sm->user->methods[idx].method; + sm->user_eap_method_index++; } else { + *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_NONE; } - wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next); + wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", + *vendor, next); return next; } @@ -824,7 +837,10 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) } if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) { + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " "available -> CONTINUE"); return DECISION_CONTINUE; @@ -848,6 +864,15 @@ static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) } +/** + * eap_sm_step - Step EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ int eap_sm_step(struct eap_sm *sm) { int res = 0; @@ -861,17 +886,14 @@ int eap_sm_step(struct eap_sm *sm) } -u8 eap_get_type(const char *name) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (strcmp(eap_methods[i]->name, name) == 0) - return eap_methods[i]->method; - } - return EAP_TYPE_NONE; -} - - +/** + * eap_set_eapRespData - Set EAP response (eapRespData) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @eapRespData: EAP-Response payload from the supplicant + * @eapRespDataLen: Length of eapRespData in bytes + * + * This function is called when an EAP-Response is received from a supplicant. + */ void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, size_t eapRespDataLen) { @@ -898,21 +920,29 @@ static void eap_user_free(struct eap_user *user) } +/** + * eap_sm_init - Allocate and initialize EAP state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. + */ struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf) + struct eap_config *conf) { struct eap_sm *sm; - sm = malloc(sizeof(*sm)); + sm = wpa_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; - memset(sm, 0, sizeof(*sm)); sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->MaxRetrans = 10; - sm->ssl_ctx = eap_conf->ssl_ctx; - sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv; - sm->backend_auth = eap_conf->backend_auth; + sm->ssl_ctx = conf->ssl_ctx; + sm->eap_sim_db_priv = conf->eap_sim_db_priv; + sm->backend_auth = conf->backend_auth; wpa_printf(MSG_DEBUG, "EAP: State machine created"); @@ -920,6 +950,13 @@ struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, } +/** + * eap_sm_deinit - Deinitialize and free an EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ void eap_sm_deinit(struct eap_sm *sm) { if (sm == NULL) @@ -937,6 +974,13 @@ void eap_sm_deinit(struct eap_sm *sm) } +/** + * eap_sm_notify_cached - Notify EAP state machine of cached PMK + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function is called when PMKSA caching is used to skip EAP + * authentication. + */ void eap_sm_notify_cached(struct eap_sm *sm) { if (sm == NULL) @@ -944,3 +988,153 @@ void eap_sm_notify_cached(struct eap_sm *sm) sm->EAP_state = EAP_SUCCESS; } + + +/** + * eap_sm_pending_cb - EAP state machine callback for a pending EAP request + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function is called when data for a pending EAP-Request is received. + */ +void eap_sm_pending_cb(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); + if (sm->method_pending == METHOD_PENDING_WAIT) + sm->method_pending = METHOD_PENDING_CONT; +} + + +/** + * eap_sm_method_pending - Query whether EAP method is waiting for pending data + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: 1 if method is waiting for pending data or 0 if not + */ +int eap_sm_method_pending(struct eap_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->method_pending == METHOD_PENDING_WAIT; +} + + +/** + * eap_hdr_validate - Validate EAP header + * @vendor: Expected EAP Vendor-Id (0 = IETF) + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @msglen: Length of msg + * @plen: Pointer to variable to contain the returned payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function to verify + * that the received EAP request packet has a valid header. This function is + * able to process both legacy and expanded EAP headers and in most cases, the + * caller can just use the returned payload pointer (into *plen) for processing + * the payload regardless of whether the packet used the expanded EAP header or + * not. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const u8 *msg, size_t msglen, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + hdr = (const struct eap_hdr *) msg; + + if (msglen < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return NULL; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + 1 || len > msglen) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return NULL; + } + + pos = (const u8 *) (hdr + 1); + + if (*pos == EAP_TYPE_EXPANDED) { + int exp_vendor; + u32 exp_type; + if (len < sizeof(*hdr) + 8) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " + "length"); + return NULL; + } + pos++; + exp_vendor = WPA_GET_BE24(pos); + pos += 3; + exp_type = WPA_GET_BE32(pos); + pos += 4; + if (exp_vendor != vendor || exp_type != (u32) eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " + "type"); + return NULL; + } + + *plen = len - sizeof(*hdr) - 8; + return pos; + } else { + if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; + } +} + + +/** + * eap_msg_alloc - Allocate a buffer for an EAP message + * @vendor: Vendor-Id (0 = IETF) + * @type: EAP type + * @len: Buffer for returning message length + * @payload_len: Payload length in bytes (data after Type) + * @code: Message Code (EAP_CODE_*) + * @identifier: Identifier + * @payload: Pointer to payload pointer that will be set to point to the + * beginning of the payload or %NULL if payload pointer is not needed + * Returns: Pointer to the allocated message buffer or %NULL on error + * + * This function can be used to allocate a buffer for an EAP message and fill + * in the EAP header. This function is automatically using expanded EAP header + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do + * not need to separately select which header type to use when using this + * function to allocate the message buffers. + */ +struct eap_hdr * eap_msg_alloc(int vendor, EapType type, size_t *len, + size_t payload_len, u8 code, u8 identifier, + u8 **payload) +{ + struct eap_hdr *hdr; + u8 *pos; + + *len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + + payload_len; + hdr = malloc(*len); + if (hdr) { + hdr->code = code; + hdr->identifier = identifier; + hdr->length = host_to_be16(*len); + pos = (u8 *) (hdr + 1); + if (vendor == EAP_VENDOR_IETF) { + *pos++ = type; + } else { + *pos++ = EAP_TYPE_EXPANDED; + WPA_PUT_BE24(pos, vendor); + pos += 3; + WPA_PUT_BE32(pos, type); + pos += 4; + } + if (payload) + *payload = pos; + } + + return hdr; +} diff --git a/contrib/hostapd/eap.h b/contrib/hostapd/eap.h index ce49b07..8fb1ec3 100644 --- a/contrib/hostapd/eap.h +++ b/contrib/hostapd/eap.h @@ -1,18 +1,38 @@ /* $FreeBSD$ */ +/* + * hostapd / EAP Standalone Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2005, 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. + */ + #ifndef EAP_H #define EAP_H #include "defs.h" #include "eap_defs.h" +#include "eap_methods.h" struct eap_sm; #define EAP_MAX_METHODS 8 struct eap_user { - u8 methods[EAP_MAX_METHODS]; + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; + int password_hash; /* whether password is hashed with + * nt_password_hash() */ int phase2; int force_version; }; @@ -48,10 +68,11 @@ struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); void eap_sm_deinit(struct eap_sm *sm); int eap_sm_step(struct eap_sm *sm); -u8 eap_get_type(const char *name); void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, size_t eapRespDataLen); void eap_sm_notify_cached(struct eap_sm *sm); +void eap_sm_pending_cb(struct eap_sm *sm); +int eap_sm_method_pending(struct eap_sm *sm); #else /* EAP_SERVER */ @@ -71,10 +92,6 @@ static inline int eap_sm_step(struct eap_sm *sm) return 0; } -static inline u8 eap_get_type(const char *name) -{ - return EAP_TYPE_NONE; -} static inline void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, @@ -86,6 +103,15 @@ static inline void eap_sm_notify_cached(struct eap_sm *sm) { } +static inline void eap_sm_pending_cb(struct eap_sm *sm) +{ +} + +static inline int eap_sm_method_pending(struct eap_sm *sm) +{ + return 0; +} + #endif /* EAP_SERVER */ #endif /* EAP_H */ diff --git a/contrib/hostapd/eapol_sm.c b/contrib/hostapd/eapol_sm.c index 4ded7e7..18f2576 100644 --- a/contrib/hostapd/eapol_sm.c +++ b/contrib/hostapd/eapol_sm.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - EAPOL state machine - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.1X Authenticator - EAPOL state machine + * Copyright (c) 2002-2005, 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 @@ -15,20 +14,21 @@ * $FreeBSD$ */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/socket.h> +#include "includes.h" #include "hostapd.h" #include "ieee802_1x.h" #include "eapol_sm.h" #include "eloop.h" #include "wpa.h" +#include "preauth.h" #include "sta_info.h" #include "eap.h" +#include "state_machine.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr static struct eapol_callbacks eapol_cb; @@ -49,25 +49,6 @@ ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0) #define processKey() do { } while (0) -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->_data.state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eapol_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); @@ -117,8 +98,8 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) SM_STATE(AUTH_PAE, INITIALIZE) { - SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae); - sm->auth_pae.portMode = Auto; + SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; sm->currentId = 255; } @@ -126,21 +107,21 @@ SM_STATE(AUTH_PAE, INITIALIZE) SM_STATE(AUTH_PAE, DISCONNECTED) { - int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE; + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; - if (sm->auth_pae.eapolLogoff) { - if (sm->auth_pae.state == AUTH_PAE_CONNECTING) - sm->auth_pae.authEapLogoffsWhileConnecting++; - else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; } - SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae); + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->auth_pae.reAuthCount = 0; - sm->auth_pae.eapolLogoff = FALSE; + sm->reAuthCount = 0; + sm->eapolLogoff = FALSE; if (!from_initialize) { if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); @@ -152,48 +133,58 @@ SM_STATE(AUTH_PAE, DISCONNECTED) SM_STATE(AUTH_PAE, RESTART) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) { + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { if (sm->reAuthenticate) - sm->auth_pae.authAuthReauthsWhileAuthenticated++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticated++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; } - SM_ENTRY(AUTH_PAE, RESTART, auth_pae); + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); - sm->auth_pae.eapRestart = TRUE; + sm->eapRestart = TRUE; ieee802_1x_request_identity(sm->hapd, sm->sta); } SM_STATE(AUTH_PAE, CONNECTING) { - if (sm->auth_pae.state != AUTH_PAE_CONNECTING) - sm->auth_pae.authEntersConnecting++; + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; - SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); sm->reAuthenticate = FALSE; - sm->auth_pae.reAuthCount++; + sm->reAuthCount++; } SM_STATE(AUTH_PAE, HELD) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail) - sm->auth_pae.authAuthFailWhileAuthenticating++; + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; - SM_ENTRY(AUTH_PAE, HELD, auth_pae); + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->quietWhile = sm->auth_pae.quietPeriod; - sm->auth_pae.eapolLogoff = FALSE; + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = FALSE; hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "authentication failed"); + HOSTAPD_LEVEL_WARNING, "authentication failed - " + "EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv)); + if (sm->eap_type_authsrv != sm->eap_type_supp) { + hostapd_logger(sm->hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_INFO, + "Supplicant used different EAP type: %d (%s)", + sm->eap_type_supp, + eap_type_text(sm->eap_type_supp)); + } if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); else @@ -203,16 +194,24 @@ SM_STATE(AUTH_PAE, HELD) SM_STATE(AUTH_PAE, AUTHENTICATED) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) - sm->auth_pae.authAuthSuccessesWhileAuthenticating++; + char *extra = ""; + + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; - SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae); + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); - sm->auth_pae.reAuthCount = 0; + sm->reAuthCount = 0; + if (sm->flags & EAPOL_SM_PREAUTH) + extra = " (pre-authentication)"; + else if (wpa_auth_sta_get_pmksa(sm->sta->wpa_sm)) + extra = " (PMKSA cache)"; hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authenticated"); + HOSTAPD_LEVEL_INFO, "authenticated - EAP type: %d (%s)" + "%s", sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv), extra); if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 1); else @@ -222,14 +221,14 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) SM_STATE(AUTH_PAE, AUTHENTICATING) { - if (sm->auth_pae.state == AUTH_PAE_CONNECTING && sm->rx_identity) { - sm->auth_pae.authEntersAuthenticating++; + if (sm->auth_pae_state == AUTH_PAE_CONNECTING && sm->rx_identity) { + sm->authEntersAuthenticating++; sm->rx_identity = FALSE; } - SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); - sm->auth_pae.eapolStart = FALSE; + sm->eapolStart = FALSE; sm->authSuccess = FALSE; sm->authFail = FALSE; sm->authTimeout = FALSE; @@ -241,16 +240,16 @@ SM_STATE(AUTH_PAE, AUTHENTICATING) SM_STATE(AUTH_PAE, ABORTING) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING) { + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { if (sm->authTimeout) - sm->auth_pae.authAuthTimeoutsWhileAuthenticating++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticating++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticating++; + sm->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; } - SM_ENTRY(AUTH_PAE, ABORTING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); sm->authAbort = TRUE; sm->keyRun = FALSE; @@ -260,44 +259,43 @@ SM_STATE(AUTH_PAE, ABORTING) SM_STATE(AUTH_PAE, FORCE_AUTH) { - SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae); + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); - sm->auth_pae.portMode = ForceAuthorized; - sm->auth_pae.eapolStart = FALSE; + sm->portMode = ForceAuthorized; + sm->eapolStart = FALSE; txCannedSuccess(); } SM_STATE(AUTH_PAE, FORCE_UNAUTH) { - SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae); + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->auth_pae.portMode = ForceUnauthorized; - sm->auth_pae.eapolStart = FALSE; + sm->portMode = ForceUnauthorized; + sm->eapolStart = FALSE; txCannedFail(); } SM_STEP(AUTH_PAE) { - if ((sm->portControl == Auto && - sm->auth_pae.portMode != sm->portControl) || + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || sm->initialize || !sm->portEnabled) SM_ENTER(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && - sm->auth_pae.portMode != sm->portControl && + sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && - sm->auth_pae.portMode != sm->portControl && + sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); else { - switch (sm->auth_pae.state) { + switch (sm->auth_pae_state) { case AUTH_PAE_INITIALIZE: SM_ENTER(AUTH_PAE, DISCONNECTED); break; @@ -305,7 +303,7 @@ SM_STEP(AUTH_PAE) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_RESTART: - if (!sm->auth_pae.eapRestart) + if (!sm->eapRestart) SM_ENTER(AUTH_PAE, CONNECTING); break; case AUTH_PAE_HELD: @@ -313,19 +311,17 @@ SM_STEP(AUTH_PAE) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_CONNECTING: - if (sm->auth_pae.eapolLogoff || - sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax) + if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); - else if ((sm->be_auth.eapReq && - sm->auth_pae.reAuthCount <= - sm->auth_pae.reAuthMax) || + else if ((sm->eapReq && + sm->reAuthCount <= sm->reAuthMax) || sm->eapSuccess || sm->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: - if (sm->auth_pae.eapolStart || sm->reAuthenticate) + if (sm->eapolStart || sm->reAuthenticate) SM_ENTER(AUTH_PAE, RESTART); - else if (sm->auth_pae.eapolLogoff || !sm->portValid) + else if (sm->eapolLogoff || !sm->portValid) SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_AUTHENTICATING: @@ -334,22 +330,22 @@ SM_STEP(AUTH_PAE) else if (sm->authFail || (sm->keyDone && !sm->portValid)) SM_ENTER(AUTH_PAE, HELD); - else if (sm->auth_pae.eapolStart || - sm->auth_pae.eapolLogoff || sm->authTimeout) + else if (sm->eapolStart || sm->eapolLogoff || + sm->authTimeout) SM_ENTER(AUTH_PAE, ABORTING); break; case AUTH_PAE_ABORTING: - if (sm->auth_pae.eapolLogoff && !sm->authAbort) + if (sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, DISCONNECTED); - else if (!sm->auth_pae.eapolLogoff && !sm->authAbort) + else if (!sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_FORCE_AUTH: - if (sm->auth_pae.eapolStart) + if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_AUTH); break; case AUTH_PAE_FORCE_UNAUTH: - if (sm->auth_pae.eapolStart) + if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); break; } @@ -362,21 +358,21 @@ SM_STEP(AUTH_PAE) SM_STATE(BE_AUTH, INITIALIZE) { - SM_ENTRY(BE_AUTH, INITIALIZE, be_auth); + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); abortAuth(); - sm->be_auth.eapNoReq = FALSE; + sm->eapNoReq = FALSE; sm->authAbort = FALSE; } SM_STATE(BE_AUTH, REQUEST) { - SM_ENTRY(BE_AUTH, REQUEST, be_auth); + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); txReq(); - sm->be_auth.eapReq = FALSE; - sm->be_auth.backendOtherRequestsToSupplicant++; + sm->eapReq = FALSE; + sm->backendOtherRequestsToSupplicant++; /* * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but @@ -395,21 +391,21 @@ SM_STATE(BE_AUTH, REQUEST) SM_STATE(BE_AUTH, RESPONSE) { - SM_ENTRY(BE_AUTH, RESPONSE, be_auth); + SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); sm->authTimeout = FALSE; sm->eapolEap = FALSE; - sm->be_auth.eapNoReq = FALSE; - sm->aWhile = sm->be_auth.serverTimeout; - sm->be_auth.eapResp = TRUE; + sm->eapNoReq = FALSE; + sm->aWhile = sm->serverTimeout; + sm->eapResp = TRUE; sendRespToServer(); - sm->be_auth.backendResponses++; + sm->backendResponses++; } SM_STATE(BE_AUTH, SUCCESS) { - SM_ENTRY(BE_AUTH, SUCCESS, be_auth); + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); txReq(); sm->authSuccess = TRUE; @@ -419,7 +415,7 @@ SM_STATE(BE_AUTH, SUCCESS) SM_STATE(BE_AUTH, FAIL) { - SM_ENTRY(BE_AUTH, FAIL, be_auth); + SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here. * txCannelFail() is used as a workaround for the case where @@ -435,7 +431,7 @@ SM_STATE(BE_AUTH, FAIL) SM_STATE(BE_AUTH, TIMEOUT) { - SM_ENTRY(BE_AUTH, TIMEOUT, be_auth); + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); sm->authTimeout = TRUE; } @@ -443,7 +439,7 @@ SM_STATE(BE_AUTH, TIMEOUT) SM_STATE(BE_AUTH, IDLE) { - SM_ENTRY(BE_AUTH, IDLE, be_auth); + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); sm->authStart = FALSE; } @@ -451,9 +447,9 @@ SM_STATE(BE_AUTH, IDLE) SM_STATE(BE_AUTH, IGNORE) { - SM_ENTRY(BE_AUTH, IGNORE, be_auth); + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); - sm->be_auth.eapNoReq = FALSE; + sm->eapNoReq = FALSE; } @@ -464,31 +460,31 @@ SM_STEP(BE_AUTH) return; } - switch (sm->be_auth.state) { + switch (sm->be_auth_state) { case BE_AUTH_INITIALIZE: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_REQUEST: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) + else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: - if (sm->be_auth.eapNoReq) + if (sm->eapNoReq) SM_ENTER(BE_AUTH, IGNORE); - if (sm->be_auth.eapReq) { - sm->be_auth.backendAccessChallenges++; + if (sm->eapReq) { + sm->backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); else if (sm->eapFail) { - sm->be_auth.backendAuthFails++; + sm->backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); } else if (sm->eapSuccess) { - sm->be_auth.backendAuthSuccesses++; + sm->backendAuthSuccesses++; SM_ENTER(BE_AUTH, SUCCESS); } break; @@ -504,7 +500,7 @@ SM_STEP(BE_AUTH) case BE_AUTH_IDLE: if (sm->eapFail && sm->authStart) SM_ENTER(BE_AUTH, FAIL); - else if (sm->be_auth.eapReq && sm->authStart) + else if (sm->eapReq && sm->authStart) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapSuccess && sm->authStart) SM_ENTER(BE_AUTH, SUCCESS); @@ -512,7 +508,7 @@ SM_STEP(BE_AUTH) case BE_AUTH_IGNORE: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) + else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); @@ -526,31 +522,30 @@ SM_STEP(BE_AUTH) SM_STATE(REAUTH_TIMER, INITIALIZE) { - SM_ENTRY(REAUTH_TIMER, INITIALIZE, reauth_timer); + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); - sm->reAuthWhen = sm->reauth_timer.reAuthPeriod; + sm->reAuthWhen = sm->reAuthPeriod; } SM_STATE(REAUTH_TIMER, REAUTHENTICATE) { - SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; - wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL); + wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL); } SM_STEP(REAUTH_TIMER) { if (sm->portControl != Auto || sm->initialize || - sm->authPortStatus == Unauthorized || - !sm->reauth_timer.reAuthEnabled) { + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { SM_ENTER(REAUTH_TIMER, INITIALIZE); return; } - switch (sm->reauth_timer.state) { + switch (sm->reauth_timer_state) { case REAUTH_TIMER_INITIALIZE: if (sm->reAuthWhen == 0) SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); @@ -567,13 +562,13 @@ SM_STEP(REAUTH_TIMER) SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) { - SM_ENTRY(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); + SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); } SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) { - SM_ENTRY(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); txKey(); sm->keyAvailable = FALSE; @@ -588,10 +583,10 @@ SM_STEP(AUTH_KEY_TX) return; } - switch (sm->auth_key_tx.state) { + switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && - !sm->sta->wpa) + !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: @@ -609,16 +604,16 @@ SM_STEP(AUTH_KEY_TX) SM_STATE(KEY_RX, NO_KEY_RECEIVE) { - SM_ENTRY(KEY_RX, NO_KEY_RECEIVE, key_rx); + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); } SM_STATE(KEY_RX, KEY_RECEIVE) { - SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx); + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); processKey(); - sm->key_rx.rxKey = FALSE; + sm->rxKey = FALSE; } @@ -629,13 +624,13 @@ SM_STEP(KEY_RX) return; } - switch (sm->key_rx.state) { + switch (sm->key_rx_state) { case KEY_RX_NO_KEY_RECEIVE: - if (sm->key_rx.rxKey) + if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; case KEY_RX_KEY_RECEIVE: - if (sm->key_rx.rxKey) + if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; } @@ -647,16 +642,15 @@ SM_STEP(KEY_RX) SM_STATE(CTRL_DIR, FORCE_BOTH) { - SM_ENTRY(CTRL_DIR, FORCE_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = Both; + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; } SM_STATE(CTRL_DIR, IN_OR_BOTH) { - SM_ENTRY(CTRL_DIR, IN_OR_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = - sm->ctrl_dir.adminControlledDirections; + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; } @@ -667,16 +661,16 @@ SM_STEP(CTRL_DIR) return; } - switch (sm->ctrl_dir.state) { + switch (sm->ctrl_dir_state) { case CTRL_DIR_FORCE_BOTH: - if (sm->portEnabled && sm->ctrl_dir.operEdge) + if (sm->portEnabled && sm->operEdge) SM_ENTER(CTRL_DIR, IN_OR_BOTH); break; case CTRL_DIR_IN_OR_BOTH: - if (sm->ctrl_dir.operControlledDirections != - sm->ctrl_dir.adminControlledDirections) + if (sm->operControlledDirections != + sm->adminControlledDirections) SM_ENTER(CTRL_DIR, IN_OR_BOTH); - if (!sm->portEnabled || !sm->ctrl_dir.operEdge) + if (!sm->portEnabled || !sm->operEdge) SM_ENTER(CTRL_DIR, FORCE_BOTH); break; } @@ -685,16 +679,15 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * -eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) +eapol_sm_alloc(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm; - sm = (struct eapol_state_machine *) malloc(sizeof(*sm)); + sm = wpa_zalloc(sizeof(*sm)); if (sm == NULL) { printf("IEEE 802.1X port state allocation failed\n"); return NULL; } - memset(sm, 0, sizeof(*sm)); sm->radius_identifier = -1; memcpy(sm->addr, sta->addr, ETH_ALEN); if (sta->flags & WLAN_STA_PREAUTH) @@ -704,23 +697,22 @@ eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) sm->sta = sta; /* Set default values for state machine constants */ - sm->auth_pae.state = AUTH_PAE_INITIALIZE; - sm->auth_pae.quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; - sm->auth_pae.reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; - sm->be_auth.state = BE_AUTH_INITIALIZE; - sm->be_auth.serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; - sm->reauth_timer.state = REAUTH_TIMER_INITIALIZE; - sm->reauth_timer.reAuthPeriod = hapd->conf->eap_reauth_period; - sm->reauth_timer.reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? - TRUE : FALSE; + sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; + sm->reAuthPeriod = hapd->conf->eap_reauth_period; + sm->reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? TRUE : FALSE; - sm->auth_key_tx.state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; - sm->key_rx.state = KEY_RX_NO_KEY_RECEIVE; + sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; - sm->ctrl_dir.state = CTRL_DIR_IN_OR_BOTH; + sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; sm->portEnabled = FALSE; sm->portControl = Auto; @@ -781,8 +773,8 @@ static void eapol_sm_step_run(struct eapol_state_machine *sm) { struct hostapd_data *hapd = sm->hapd; u8 addr[ETH_ALEN]; - int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, - prev_key_rx, prev_ctrl_dir; + unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, + prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; int max_steps = 100; memcpy(addr, sm->sta->addr, ETH_ALEN); @@ -794,12 +786,12 @@ static void eapol_sm_step_run(struct eapol_state_machine *sm) * eloop callback. */ restart: - prev_auth_pae = sm->auth_pae.state; - prev_be_auth = sm->be_auth.state; - prev_reauth_timer = sm->reauth_timer.state; - prev_auth_key_tx = sm->auth_key_tx.state; - prev_key_rx = sm->key_rx.state; - prev_ctrl_dir = sm->ctrl_dir.state; + prev_auth_pae = sm->auth_pae_state; + prev_be_auth = sm->be_auth_state; + prev_reauth_timer = sm->reauth_timer_state; + prev_auth_key_tx = sm->auth_key_tx_state; + prev_key_rx = sm->key_rx_state; + prev_ctrl_dir = sm->ctrl_dir_state; SM_STEP_RUN(AUTH_PAE); if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) @@ -813,12 +805,12 @@ restart: if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(CTRL_DIR); - if (prev_auth_pae != sm->auth_pae.state || - prev_be_auth != sm->be_auth.state || - prev_reauth_timer != sm->reauth_timer.state || - prev_auth_key_tx != sm->auth_key_tx.state || - prev_key_rx != sm->key_rx.state || - prev_ctrl_dir != sm->ctrl_dir.state) { + if (prev_auth_pae != sm->auth_pae_state || + prev_be_auth != sm->be_auth_state || + prev_reauth_timer != sm->reauth_timer_state || + prev_auth_key_tx != sm->auth_key_tx_state || + prev_key_rx != sm->key_rx_state || + prev_ctrl_dir != sm->ctrl_dir_state) { if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ @@ -837,7 +829,7 @@ restart: } if (eapol_sm_sta_entry_alive(hapd, addr)) - wpa_sm_notify(sm->hapd, sm->sta); + wpa_auth_sm_notify(sm->sta->wpa_sm); } @@ -1024,22 +1016,21 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s authAuthReauthsWhileAuthenticated=%d\n" "%s authAuthEapStartsWhileAuthenticated=%d\n" "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae.state), prefix, - _SB(sm->auth_pae.eapolLogoff), _SB(sm->auth_pae.eapolStart), - _SB(sm->auth_pae.eapRestart), prefix, - port_type_txt(sm->auth_pae.portMode), sm->auth_pae.reAuthCount, - prefix, sm->auth_pae.quietPeriod, sm->auth_pae.reAuthMax, - prefix, sm->auth_pae.authEntersConnecting, - prefix, sm->auth_pae.authEapLogoffsWhileConnecting, - prefix, sm->auth_pae.authEntersAuthenticating, - prefix, sm->auth_pae.authAuthSuccessesWhileAuthenticating, - prefix, sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - prefix, sm->auth_pae.authAuthFailWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - prefix, sm->auth_pae.authAuthReauthsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticated); + prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, + _SB(sm->eapolLogoff), _SB(sm->eapolStart), _SB(sm->eapRestart), + prefix, port_type_txt(sm->portMode), sm->reAuthCount, + prefix, sm->quietPeriod, sm->reAuthMax, + prefix, sm->authEntersConnecting, + prefix, sm->authEapLogoffsWhileConnecting, + prefix, sm->authEntersAuthenticating, + prefix, sm->authAuthSuccessesWhileAuthenticating, + prefix, sm->authAuthTimeoutsWhileAuthenticating, + prefix, sm->authAuthFailWhileAuthenticating, + prefix, sm->authAuthEapStartsWhileAuthenticating, + prefix, sm->authAuthEapLogoffWhileAuthenticating, + prefix, sm->authAuthReauthsWhileAuthenticated, + prefix, sm->authAuthEapStartsWhileAuthenticated, + prefix, sm->authAuthEapLogoffWhileAuthenticated); fprintf(f, "%s Backend Authentication:\n" "%s state=%s\n" @@ -1051,42 +1042,39 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s backendAuthSuccesses=%d\n" "%s backendAuthFails=%d\n", prefix, prefix, - be_auth_state_txt(sm->be_auth.state), - prefix, _SB(sm->be_auth.eapNoReq), _SB(sm->be_auth.eapReq), - _SB(sm->be_auth.eapResp), - prefix, sm->be_auth.serverTimeout, - prefix, sm->be_auth.backendResponses, - prefix, sm->be_auth.backendAccessChallenges, - prefix, sm->be_auth.backendOtherRequestsToSupplicant, - prefix, sm->be_auth.backendAuthSuccesses, - prefix, sm->be_auth.backendAuthFails); + be_auth_state_txt(sm->be_auth_state), + prefix, _SB(sm->eapNoReq), _SB(sm->eapReq), _SB(sm->eapResp), + prefix, sm->serverTimeout, + prefix, sm->backendResponses, + prefix, sm->backendAccessChallenges, + prefix, sm->backendOtherRequestsToSupplicant, + prefix, sm->backendAuthSuccesses, + prefix, sm->backendAuthFails); fprintf(f, "%s Reauthentication Timer:\n" "%s state=%s\n" "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer.state), prefix, - sm->reauth_timer.reAuthPeriod, - _SB(sm->reauth_timer.reAuthEnabled)); + reauth_timer_state_txt(sm->reauth_timer_state), prefix, + sm->reAuthPeriod, _SB(sm->reAuthEnabled)); fprintf(f, "%s Authenticator Key Transmit:\n" "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx.state)); + auth_key_tx_state_txt(sm->auth_key_tx_state)); fprintf(f, "%s Key Receive:\n" "%s state=%s\n" "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx.state), - prefix, _SB(sm->key_rx.rxKey)); + key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); fprintf(f, "%s Controlled Directions:\n" "%s state=%s\n" "%s adminControlledDirections=%s " "operControlledDirections=%s\n" "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir.state), - prefix, ctrl_dir_txt(sm->ctrl_dir.adminControlledDirections), - ctrl_dir_txt(sm->ctrl_dir.operControlledDirections), - prefix, _SB(sm->ctrl_dir.operEdge)); + ctrl_dir_state_txt(sm->ctrl_dir_state), + prefix, ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + prefix, _SB(sm->operEdge)); #undef _SB } #endif /* HOSTAPD_DUMP_STATE */ @@ -1101,15 +1089,15 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) case EAPOL_eapSuccess: return sm->eapSuccess; case EAPOL_eapRestart: - return sm->auth_pae.eapRestart; + return sm->eapRestart; case EAPOL_eapFail: return sm->eapFail; case EAPOL_eapResp: - return sm->be_auth.eapResp; + return sm->eapResp; case EAPOL_eapReq: - return sm->be_auth.eapReq; + return sm->eapReq; case EAPOL_eapNoReq: - return sm->be_auth.eapNoReq; + return sm->eapNoReq; case EAPOL_portEnabled: return sm->portEnabled; case EAPOL_eapTimeout: @@ -1130,19 +1118,19 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, sm->eapSuccess = value; break; case EAPOL_eapRestart: - sm->auth_pae.eapRestart = value; + sm->eapRestart = value; break; case EAPOL_eapFail: sm->eapFail = value; break; case EAPOL_eapResp: - sm->be_auth.eapResp = value; + sm->eapResp = value; break; case EAPOL_eapReq: - sm->be_auth.eapReq = value; + sm->eapReq = value; break; case EAPOL_eapNoReq: - sm->be_auth.eapNoReq = value; + sm->eapNoReq = value; break; case EAPOL_portEnabled: sm->portEnabled = value; @@ -1216,6 +1204,7 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, { struct eapol_state_machine *sm = ctx; const struct hostapd_eap_user *eap_user; + int i, count; eap_user = hostapd_get_eap_user(sm->hapd->conf, identity, identity_len, phase2); @@ -1224,9 +1213,13 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, memset(user, 0, sizeof(*user)); user->phase2 = phase2; - 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); @@ -1259,3 +1252,15 @@ static struct eapol_callbacks eapol_cb = .get_eap_user = eapol_sm_get_eap_user, .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, }; + + +int eapol_sm_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_sm_step(sm); + + return 0; +} diff --git a/contrib/hostapd/ieee802_1x.c b/contrib/hostapd/ieee802_1x.c index c8b1602..7cde0652 100644 --- a/contrib/hostapd/ieee802_1x.c +++ b/contrib/hostapd/ieee802_1x.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.1X Authenticator + * Copyright (c) 2002-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 @@ -15,17 +14,8 @@ * $FreeBSD$ */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/ioctl.h> -#include <signal.h> +#include "includes.h" #include <assert.h> -#include <time.h> -#include <sys/time.h> -#include <sys/socket.h> #include "hostapd.h" #include "ieee802_1x.h" @@ -38,7 +28,10 @@ #include "eloop.h" #include "sta_info.h" #include "wpa.h" +#include "preauth.h" +#include "pmksa_cache.h" #include "driver.h" +#include "hw_features.h" #include "eap.h" @@ -46,8 +39,8 @@ static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, struct sta_info *sta); -static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, - u8 *data, size_t datalen) +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, u8 *data, size_t datalen) { u8 *buf; struct ieee802_1x_hdr *xhdr; @@ -55,14 +48,13 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, int encrypt = 0; len = sizeof(*xhdr) + datalen; - buf = malloc(len); + buf = wpa_zalloc(len); if (buf == NULL) { printf("malloc() failed for ieee802_1x_send(len=%lu)\n", (unsigned long) len); return; } - memset(buf, 0, len); #if 0 /* TODO: * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any @@ -76,14 +68,14 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, #endif xhdr = (struct ieee802_1x_hdr *) buf; - xhdr->version = EAPOL_VERSION; + xhdr->version = hapd->conf->eapol_version; xhdr->type = type; xhdr->length = htons(datalen); if (datalen > 0 && data != NULL) memcpy(xhdr + 1, data, datalen); - if (sta->wpa_sm && sta->wpa_sm->pairwise_set) + if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); @@ -95,23 +87,33 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, } -void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, - int authorized) +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) { + int res; + if (sta->flags & WLAN_STA_PREAUTH) return; if (authorized) { sta->flags |= WLAN_STA_AUTHORIZED; + res = hostapd_sta_set_flags(hapd, sta->addr, + WLAN_STA_AUTHORIZED, ~0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { sta->flags &= ~WLAN_STA_AUTHORIZED; + res = hostapd_sta_set_flags(hapd, sta->addr, + 0, ~WLAN_STA_AUTHORIZED); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); } - hostapd_set_sta_authorized(hapd, sta->addr, authorized); + if (res && errno != ENOENT) { + printf("Could not set station " MACSTR " flags for kernel " + "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + } + if (authorized) accounting_sta_start(hapd, sta); } @@ -146,21 +148,19 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, return; } - if (sm == NULL || !sm->auth_pae.eapRestart) + if (sm == NULL || !sm->eapRestart) return; ieee802_1x_new_auth_session(hapd, sta); tlen = sizeof(*eap) + 1 + hapd->conf->eap_req_id_text_len; - buf = malloc(tlen); + buf = wpa_zalloc(tlen); if (buf == NULL) { printf("Could not allocate memory for identity request\n"); return; } - memset(buf, 0, tlen); - eap = (struct eap_hdr *) buf; eap->code = EAP_CODE_REQUEST; eap->identifier = ++sm->currentId; @@ -172,7 +172,7 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, hapd->conf->eap_req_id_text_len); } - sm->be_auth.eapReq = TRUE; + sm->eapReq = TRUE; free(sm->last_eap_radius); sm->last_eap_radius = buf; sm->last_eap_radius_len = tlen; @@ -186,7 +186,7 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, " (identifier %d, timeout 30)\n", MAC2STR(sta->addr), eap->identifier); - sm->auth_pae.eapRestart = FALSE; + sm->eapRestart = FALSE; } @@ -250,24 +250,9 @@ void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta) } -void hostapd_get_ntp_timestamp(u8 *buf) -{ - struct timeval now; - u32 sec, usec; - - /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ - gettimeofday(&now, NULL); - sec = htonl(now.tv_sec + 2208988800U); /* Epoch to 1900 */ - /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ - usec = now.tv_usec; - usec = htonl(4295 * usec - (usec >> 5) - (usec >> 9)); - memcpy(buf, (u8 *) &sec, 4); - memcpy(buf + 4, (u8 *) &usec, 4); -} - - -static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, - int index, int broadcast, +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, + struct sta_info *sta, + int idx, int broadcast, u8 *key_data, size_t key_len) { u8 *buf, *ekey; @@ -280,16 +265,15 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, return; len = sizeof(*key) + key_len; - buf = malloc(sizeof(*hdr) + len); + buf = wpa_zalloc(sizeof(*hdr) + len); if (buf == NULL) return; - memset(buf, 0, sizeof(*hdr) + len); hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; key->key_length = htons(key_len); - hostapd_get_ntp_timestamp(key->replay_counter); + wpa_get_ntp_timestamp(key->replay_counter); if (hostapd_get_rand(key->key_iv, sizeof(key->key_iv))) { printf("Could not get random numbers\n"); @@ -297,7 +281,7 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, return; } - key->key_index = index | (broadcast ? 0 : BIT(7)); + key->key_index = idx | (broadcast ? 0 : BIT(7)); if (hapd->conf->eapol_key_index_workaround) { /* According to some information, WinXP Supplicant seems to * interpret bit7 as an indication whether the key is to be @@ -324,7 +308,7 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, /* This header is needed here for HMAC-MD5, but it will be regenerated * in ieee802_1x_send() */ - hdr->version = EAPOL_VERSION; + hdr->version = hapd->conf->eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = htons(len); hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len, @@ -334,7 +318,7 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR " (%s index=%d)\n", MAC2STR(sm->addr), - broadcast ? "broadcast" : "unicast", index); + broadcast ? "broadcast" : "unicast", idx); ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapolFramesTx++; @@ -342,9 +326,109 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, } +static struct hostapd_wep_keys * +ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) +{ + struct hostapd_wep_keys *key; + + key = wpa_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->default_len = hapd->conf->default_wep_key_len; + + if (key->idx >= hapd->conf->broadcast_key_idx_max || + key->idx < hapd->conf->broadcast_key_idx_min) + key->idx = hapd->conf->broadcast_key_idx_min; + else + key->idx++; + + if (!key->key[key->idx]) + key->key[key->idx] = malloc(key->default_len); + if (key->key[key->idx] == NULL || + hostapd_get_rand(key->key[key->idx], key->default_len)) { + printf("Could not generate random WEP key (dynamic VLAN).\n"); + free(key->key[key->idx]); + key->key[key->idx] = NULL; + free(key); + return NULL; + } + key->len[key->idx] = key->default_len; + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + printf("%s: Default WEP idx %d for dynamic VLAN\n", + ifname, key->idx); + wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", + key->key[key->idx], key->len[key->idx]); + } + + if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, + key->key[key->idx], key->len[key->idx], 1)) + printf("Could not set dynamic VLAN WEP encryption key.\n"); + + hostapd_set_ieee8021x(ifname, hapd, 1); + + return key; +} + + +static struct hostapd_wep_keys * +ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, + size_t vlan_id) +{ + const char *ifname; + + if (vlan_id == 0) + return &ssid->wep; + + if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && + ssid->dyn_vlan_keys[vlan_id]) + return ssid->dyn_vlan_keys[vlan_id]; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Creating new group " + "state machine for VLAN ID %lu\n", + (unsigned long) vlan_id); + + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Unknown " + "VLAN ID %lu - cannot create group key state " + "machine\n", (unsigned long) vlan_id); + return NULL; + } + + if (ssid->dyn_vlan_keys == NULL) { + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + ssid->dyn_vlan_keys = wpa_zalloc(size); + if (ssid->dyn_vlan_keys == NULL) + return NULL; + ssid->max_dyn_vlan_keys = vlan_id; + } + + if (ssid->max_dyn_vlan_keys < vlan_id) { + struct hostapd_wep_keys **na; + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + na = realloc(ssid->dyn_vlan_keys, size); + if (na == NULL) + return NULL; + ssid->dyn_vlan_keys = na; + memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, + (vlan_id - ssid->max_dyn_vlan_keys) * + sizeof(ssid->dyn_vlan_keys[0])); + ssid->max_dyn_vlan_keys = vlan_id; + } + + ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); + + return ssid->dyn_vlan_keys[vlan_id]; +} + + void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { + struct hostapd_wep_keys *key = NULL; struct eapol_state_machine *sm = sta->eapol_sm; + int vlan_id; if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt) return; @@ -353,10 +437,21 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n", MAC2STR(sta->addr)); - if (hapd->default_wep_key) + vlan_id = sta->vlan_id; + if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) + vlan_id = 0; + + if (vlan_id) { + key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); + if (key && key->key[key->idx]) + ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, + key->key[key->idx], + key->len[key->idx]); + } else if (hapd->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, hapd->default_wep_key, hapd->conf->default_wep_key_len); + } if (hapd->conf->individual_wep_key_len > 0) { u8 *ikey; @@ -370,19 +465,18 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) return; } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) - hostapd_hexdump("Individual WEP key", - ikey, - hapd->conf->individual_wep_key_len); + wpa_hexdump_key(MSG_DEBUG, "Individual WEP key", + ikey, hapd->conf->individual_wep_key_len); ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, hapd->conf->individual_wep_key_len); /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ - if (hostapd_set_encryption(hapd, "WEP", sta->addr, 0, ikey, - hapd->conf-> - individual_wep_key_len)) { + if (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", + sta->addr, 0, ikey, + hapd->conf->individual_wep_key_len, + 1)) { printf("Could not set individual WEP encryption.\n"); } @@ -391,7 +485,38 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } -static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, +const char *radius_mode_txt(struct hostapd_data *hapd) +{ + if (hapd->iface->current_mode == NULL) + return "802.11"; + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211B: + default: + return "802.11b"; + } +} + + +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i; + u8 rate = 0; + + for (i = 0; i < sta->supported_rates_len; i++) + if ((sta->supported_rates[i] & 0x7f) > rate) + rate = sta->supported_rates[i] & 0x7f; + + return rate; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eap, size_t len) { struct radius_msg *msg; @@ -451,7 +576,8 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, } snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, (u8 *) buf, strlen(buf))) { printf("Could not add Called-Station-Id\n"); @@ -460,6 +586,7 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, (u8 *) buf, strlen(buf))) { printf("Could not add Calling-Station-Id\n"); @@ -480,7 +607,15 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, goto fail; } - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (sta->flags & WLAN_STA_PREAUTH) { + snprintf(buf, sizeof(buf), "IEEE 802.11i Pre-Authentication"); + } else { + snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + } + buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, strlen(buf))) { printf("Could not add Connect-Info\n"); @@ -518,7 +653,7 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, } -static char *eap_type_text(u8 type) +char *eap_type_text(u8 type) { switch (type) { case EAP_TYPE_IDENTITY: return "Identity"; @@ -530,13 +665,18 @@ static char *eap_type_text(u8 type) case EAP_TYPE_TLS: return "TLS"; case EAP_TYPE_TTLS: return "TTLS"; case EAP_TYPE_PEAP: return "PEAP"; + case EAP_TYPE_SIM: return "SIM"; + case EAP_TYPE_FAST: return "FAST"; + case EAP_TYPE_SAKE: return "SAKE"; + case EAP_TYPE_PSK: return "PSK"; default: return "Unknown"; } } -static void handle_eap_response(hostapd *hapd, struct sta_info *sta, - struct eap_hdr *eap, u8 *data, size_t len) +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + u8 *data, size_t len) { u8 type; struct eapol_state_machine *sm = sta->eapol_sm; @@ -569,7 +709,7 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, memcpy(sm->last_eap_supp, eap, sizeof(*eap)); memcpy(sm->last_eap_supp + sizeof(*eap), data, len); - type = data[0]; + sm->eap_type_supp = type = data[0]; data++; len--; @@ -581,7 +721,7 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, if (type == EAP_TYPE_IDENTITY) { char *buf, *pos; - int i; + size_t i; buf = malloc(4 * len + 1); if (buf) { pos = buf; @@ -620,8 +760,8 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, /* Process incoming EAP packet from Supplicant */ -static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len) +static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) { struct eap_hdr *eap; u16 eap_len; @@ -679,6 +819,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; u16 datalen; + struct rsn_pmksa_cache_entry *pmksa; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) return; @@ -726,11 +867,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN)) { - wpa_receive(hapd, sta, (u8 *) hdr, sizeof(*hdr) + datalen); + wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr, + sizeof(*hdr) + datalen); return; } - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + if (!hapd->conf->ieee802_1x || + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_PSK) return; if (!sta->eapol_sm) { @@ -755,16 +898,17 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " "from STA"); - if (sta->pmksa) { + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "cached PMKSA " "available - ignore it since " "STA sent EAPOL-Start"); - sta->pmksa = NULL; + wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); } - sta->eapol_sm->auth_pae.eapolStart = TRUE; + sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; - wpa_sm_event(hapd, sta, WPA_REAUTH_EAPOL); + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; case IEEE802_1X_TYPE_EAPOL_LOGOFF: @@ -773,7 +917,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, "from STA"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - sta->eapol_sm->auth_pae.eapolLogoff = TRUE; + sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; break; @@ -803,9 +947,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) { - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + struct rsn_pmksa_cache_entry *pmksa; + int reassoc = 1; + + if (!hapd->conf->ieee802_1x || + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_PSK) return; if (sta->eapol_sm == NULL) { @@ -819,11 +967,15 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) "failed to allocate state machine"); return; } + reassoc = 0; } sta->eapol_sm->portEnabled = TRUE; - if (sta->pmksa) { + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + int old_vlanid; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); @@ -831,19 +983,33 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) * because of existing PMKSA information in the cache. */ sta->eapol_sm->keyRun = TRUE; sta->eapol_sm->keyAvailable = TRUE; - sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; - sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - } else + old_vlanid = sta->vlan_id; + pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + ap_sta_bind_vlan(hapd, sta, old_vlanid); + } else { + if (reassoc) { + /* + * Force EAPOL state machines to start + * re-authentication without having to wait for the + * Supplicant to send EAPOL-Start. + */ + sta->eapol_sm->reAuthenticate = TRUE; + } eapol_sm_step(sta->eapol_sm); + } } void ieee802_1x_free_radius_class(struct radius_class_data *class) { - int i; + size_t i; if (class == NULL) return; for (i = 0; i < class->count; i++) @@ -862,11 +1028,10 @@ int ieee802_1x_copy_radius_class(struct radius_class_data *dst, if (src->attr == NULL) return 0; - dst->attr = malloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = wpa_zalloc(src->count * sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; - memset(dst->attr, 0, src->count * sizeof(struct radius_attr_data)); dst->count = 0; for (i = 0; i < src->count; i++) { @@ -908,7 +1073,8 @@ void ieee802_1x_free_station(struct sta_info *sta) } -static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) +static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta) { u8 *eap; size_t len; @@ -920,7 +1086,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) if (sm == NULL || sm->last_recv_radius == NULL) { if (sm) - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -937,7 +1103,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) free(sm->last_eap_radius); sm->last_eap_radius = NULL; sm->last_eap_radius_len = 0; - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -946,7 +1112,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); free(eap); - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -956,6 +1122,8 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) hdr = (struct eap_hdr *) eap; switch (hdr->code) { case EAP_CODE_REQUEST: + if (eap_type >= 0) + sm->eap_type_authsrv = eap_type; snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", eap_type >= 0 ? eap_type_text(eap_type) : "??", eap_type); @@ -975,11 +1143,12 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) snprintf(buf, sizeof(buf), "unknown EAP code"); break; } + buf[sizeof(buf) - 1] = '\0'; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " "id=%d len=%d) from RADIUS server: %s", hdr->code, hdr->identifier, ntohs(hdr->length), buf); - sm->be_auth.eapReq = TRUE; + sm->eapReq = TRUE; free(sm->last_eap_radius); sm->last_eap_radius = eap; @@ -987,8 +1156,9 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) } -static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, - struct radius_msg *msg, struct radius_msg *req, +static void ieee802_1x_get_keys(struct hostapd_data *hapd, + struct sta_info *sta, struct radius_msg *msg, + struct radius_msg *req, u8 *shared_secret, size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; @@ -1000,21 +1170,13 @@ static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, shared_secret_len); if (keys) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->send) { - size_t i; - printf("MS-MPPE-Send-Key (len=%lu):", - (unsigned long) keys->send_len); - for (i = 0; i < keys->send_len; i++) - printf(" %02x", keys->send[i]); - printf("\n"); + if (keys->send) { + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->recv) { - size_t i; - printf("MS-MPPE-Recv-Key (len=%lu):", - (unsigned long) keys->recv_len); - for (i = 0; i < keys->recv_len; i++) - printf(" %02x", keys->recv[i]); - printf("\n"); + if (keys->recv) { + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); } if (keys->send && keys->recv) { @@ -1057,12 +1219,11 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = malloc(count * sizeof(struct radius_attr_data)); + nclass = wpa_zalloc(count * sizeof(struct radius_attr_data)); if (nclass == NULL) return; nclass_count = 0; - memset(nclass, 0, count * sizeof(struct radius_attr_data)); class = NULL; for (i = 0; i < count; i++) { @@ -1170,7 +1331,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set; + int session_timeout_set, old_vlanid = 0; int eap_timeout; struct eapol_state_machine *sm; int override_eapReq = 0; @@ -1244,10 +1405,35 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else { + old_vlanid = sta->vlan_id; + sta->vlan_id = radius_msg_get_vlanid(msg); + } + if (sta->vlan_id > 0 && + hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authentication " + "server did not include required VLAN " + "ID in Access-Accept"); + break; + } + + ap_sta_bind_vlan(hapd, sta, old_vlanid); + /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { - sm->reauth_timer.reAuthPeriod = session_timeout; + sm->reAuthPeriod = session_timeout; } else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); @@ -1257,10 +1443,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); - if (sm->keyAvailable) { - pmksa_cache_add(hapd, sta, sm->eapol_key_crypt, - session_timeout_set ? - session_timeout : -1); + if (sm->keyAvailable && + wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, + session_timeout_set ? + (int) session_timeout : -1, sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry"); } break; case RADIUS_CODE_ACCESS_REJECT: @@ -1287,7 +1476,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_decapsulate_radius(hapd, sta); if (override_eapReq) - sm->be_auth.eapReq = FALSE; + sm->eapReq = FALSE; eapol_sm_step(sm); @@ -1297,7 +1486,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, /* Handler for EAPOL Backend Authentication state machine sendRespToServer. * Forward the EAP Response from Supplicant to Authentication Server. */ -void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta) +void ieee802_1x_send_resp_to_server(struct hostapd_data *hapd, + struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) @@ -1336,21 +1526,16 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) } -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled) +#ifdef HOSTAPD_DUMP_STATE +static void fprint_char(FILE *f, char c) { - if (!sta->eapol_sm) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: station " MACSTR " port %s\n", - MAC2STR(sta->addr), enabled ? "enabled" : "disabled"); - sta->eapol_sm->portEnabled = enabled ? TRUE : FALSE; - eapol_sm_step(sta->eapol_sm); + if (c >= 32 && c < 127) + fprintf(f, "%c", c); + else + fprintf(f, "<%02x>", c); } -#ifdef HOSTAPD_DUMP_STATE void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; @@ -1367,6 +1552,11 @@ void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) fprintf(f, "\n"); } + fprintf(f, "%slast EAP type: Authentication Server: %d (%s) " + "Supplicant: %d (%s)\n", prefix, + sm->eap_type_authsrv, eap_type_text(sm->eap_type_authsrv), + sm->eap_type_supp, eap_type_text(sm->eap_type_supp)); + fprintf(f, "%scached_packets=%s%s%s\n", prefix, sm->last_recv_radius ? "[RX RADIUS]" : "", sm->last_eap_radius ? "[EAP RADIUS]" : "", @@ -1377,7 +1567,7 @@ void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) #endif /* HOSTAPD_DUMP_STATE */ -static int ieee802_1x_rekey_broadcast(hostapd *hapd) +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) { if (hapd->conf->default_wep_key_len < 1) return 0; @@ -1393,11 +1583,9 @@ static int ieee802_1x_rekey_broadcast(hostapd *hapd) return -1; } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("IEEE 802.1X: New default WEP key", - hapd->default_wep_key, - hapd->conf->default_wep_key_len); - } + wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key", + hapd->default_wep_key, + hapd->conf->default_wep_key_len); return 0; } @@ -1438,10 +1626,10 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ - if (hostapd_set_encryption(hapd, "WEP", NULL, + if (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", NULL, hapd->default_wep_key_idx, hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { + hapd->conf->default_wep_key_len, 1)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); @@ -1459,12 +1647,12 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) } -int ieee802_1x_init(hostapd *hapd) +int ieee802_1x_init(struct hostapd_data *hapd) { int i; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && - hostapd_set_ieee8021x(hapd, 1)) + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1)) return -1; if (radius_client_register(hapd->radius, RADIUS_AUTH, @@ -1472,8 +1660,11 @@ int ieee802_1x_init(hostapd *hapd) return -1; if (hapd->conf->default_wep_key_len) { + hostapd_set_privacy(hapd, 1); + for (i = 0; i < 4; i++) - hostapd_set_encryption(hapd, "none", NULL, i, NULL, 0); + hostapd_set_encryption(hapd->conf->iface, hapd, + "none", NULL, i, NULL, 0, 0); ieee802_1x_rekey(hapd, NULL); @@ -1485,11 +1676,20 @@ int ieee802_1x_init(hostapd *hapd) } -void ieee802_1x_deinit(hostapd *hapd) +void ieee802_1x_deinit(struct hostapd_data *hapd) { if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hostapd_set_ieee8021x(hapd, 0); + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 0); +} + + +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + ieee802_1x_deinit(hapd); + return ieee802_1x_init(hapd); } @@ -1515,8 +1715,8 @@ static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, } -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack) +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len, int ack) { struct ieee80211_hdr *hdr; struct ieee802_1x_hdr *xhdr; @@ -1587,7 +1787,7 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx) { if (sm == NULL || sm->radius_class.attr == NULL || - idx >= sm->radius_class.count) + idx >= (int) sm->radius_class.count) return NULL; *len = sm->radius_class.attr[idx].len; @@ -1652,124 +1852,140 @@ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { - int len = 0; + int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return 0; - len += snprintf(buf + len, buflen - len, - "dot1xPaePortNumber=%d\n" - "dot1xPaePortProtocolVersion=%d\n" - "dot1xPaePortCapabilities=1\n" - "dot1xPaePortInitialize=%d\n" - "dot1xPaePortReauthenticate=FALSE\n", - sta->aid, - EAPOL_VERSION, - sm->initialize); + ret = snprintf(buf + len, buflen - len, + "dot1xPaePortNumber=%d\n" + "dot1xPaePortProtocolVersion=%d\n" + "dot1xPaePortCapabilities=1\n" + "dot1xPaePortInitialize=%d\n" + "dot1xPaePortReauthenticate=FALSE\n", + sta->aid, + EAPOL_VERSION, + sm->initialize); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthConfigTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthPaeState=%d\n" - "dot1xAuthBackendAuthState=%d\n" - "dot1xAuthAdminControlledDirections=%d\n" - "dot1xAuthOperControlledDirections=%d\n" - "dot1xAuthAuthControlledPortStatus=%d\n" - "dot1xAuthAuthControlledPortControl=%d\n" - "dot1xAuthQuietPeriod=%u\n" - "dot1xAuthServerTimeout=%u\n" - "dot1xAuthReAuthPeriod=%u\n" - "dot1xAuthReAuthEnabled=%s\n" - "dot1xAuthKeyTxEnabled=%s\n", - sm->auth_pae.state + 1, - sm->be_auth.state + 1, - sm->ctrl_dir.adminControlledDirections, - sm->ctrl_dir.operControlledDirections, - sm->authPortStatus, - sm->portControl, - sm->auth_pae.quietPeriod, - sm->be_auth.serverTimeout, - sm->reauth_timer.reAuthPeriod, - bool_txt(sm->reauth_timer.reAuthEnabled), - bool_txt(sm->keyTxEnabled)); + ret = snprintf(buf + len, buflen - len, + "dot1xAuthPaeState=%d\n" + "dot1xAuthBackendAuthState=%d\n" + "dot1xAuthAdminControlledDirections=%d\n" + "dot1xAuthOperControlledDirections=%d\n" + "dot1xAuthAuthControlledPortStatus=%d\n" + "dot1xAuthAuthControlledPortControl=%d\n" + "dot1xAuthQuietPeriod=%u\n" + "dot1xAuthServerTimeout=%u\n" + "dot1xAuthReAuthPeriod=%u\n" + "dot1xAuthReAuthEnabled=%s\n" + "dot1xAuthKeyTxEnabled=%s\n", + sm->auth_pae_state + 1, + sm->be_auth_state + 1, + sm->adminControlledDirections, + sm->operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->quietPeriod, + sm->serverTimeout, + sm->reAuthPeriod, + bool_txt(sm->reAuthEnabled), + bool_txt(sm->keyTxEnabled)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthStatsTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthEapolFramesRx=%u\n" - "dot1xAuthEapolFramesTx=%u\n" - "dot1xAuthEapolStartFramesRx=%u\n" - "dot1xAuthEapolLogoffFramesRx=%u\n" - "dot1xAuthEapolRespIdFramesRx=%u\n" - "dot1xAuthEapolRespFramesRx=%u\n" - "dot1xAuthEapolReqIdFramesTx=%u\n" - "dot1xAuthEapolReqFramesTx=%u\n" - "dot1xAuthInvalidEapolFramesRx=%u\n" - "dot1xAuthEapLengthErrorFramesRx=%u\n" - "dot1xAuthLastEapolFrameVersion=%u\n" - "dot1xAuthLastEapolFrameSource=" MACSTR "\n", - sm->dot1xAuthEapolFramesRx, - sm->dot1xAuthEapolFramesTx, - sm->dot1xAuthEapolStartFramesRx, - sm->dot1xAuthEapolLogoffFramesRx, - sm->dot1xAuthEapolRespIdFramesRx, - sm->dot1xAuthEapolRespFramesRx, - sm->dot1xAuthEapolReqIdFramesTx, - sm->dot1xAuthEapolReqFramesTx, - sm->dot1xAuthInvalidEapolFramesRx, - sm->dot1xAuthEapLengthErrorFramesRx, - sm->dot1xAuthLastEapolFrameVersion, - MAC2STR(sm->addr)); + ret = snprintf(buf + len, buflen - len, + "dot1xAuthEapolFramesRx=%u\n" + "dot1xAuthEapolFramesTx=%u\n" + "dot1xAuthEapolStartFramesRx=%u\n" + "dot1xAuthEapolLogoffFramesRx=%u\n" + "dot1xAuthEapolRespIdFramesRx=%u\n" + "dot1xAuthEapolRespFramesRx=%u\n" + "dot1xAuthEapolReqIdFramesTx=%u\n" + "dot1xAuthEapolReqFramesTx=%u\n" + "dot1xAuthInvalidEapolFramesRx=%u\n" + "dot1xAuthEapLengthErrorFramesRx=%u\n" + "dot1xAuthLastEapolFrameVersion=%u\n" + "dot1xAuthLastEapolFrameSource=" MACSTR "\n", + sm->dot1xAuthEapolFramesRx, + sm->dot1xAuthEapolFramesTx, + sm->dot1xAuthEapolStartFramesRx, + sm->dot1xAuthEapolLogoffFramesRx, + sm->dot1xAuthEapolRespIdFramesRx, + sm->dot1xAuthEapolRespFramesRx, + sm->dot1xAuthEapolReqIdFramesTx, + sm->dot1xAuthEapolReqFramesTx, + sm->dot1xAuthInvalidEapolFramesRx, + sm->dot1xAuthEapLengthErrorFramesRx, + sm->dot1xAuthLastEapolFrameVersion, + MAC2STR(sm->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthDiagTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthEntersConnecting=%u\n" - "dot1xAuthEapLogoffsWhileConnecting=%u\n" - "dot1xAuthEntersAuthenticating=%u\n" - "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" - "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" - "dot1xAuthAuthFailWhileAuthenticating=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" - "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" - "dot1xAuthBackendResponses=%u\n" - "dot1xAuthBackendAccessChallenges=%u\n" - "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" - "dot1xAuthBackendAuthSuccesses=%u\n" - "dot1xAuthBackendAuthFails=%u\n", - sm->auth_pae.authEntersConnecting, - sm->auth_pae.authEapLogoffsWhileConnecting, - sm->auth_pae.authEntersAuthenticating, - sm->auth_pae.authAuthSuccessesWhileAuthenticating, - sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - sm->auth_pae.authAuthFailWhileAuthenticating, - sm->auth_pae.authAuthEapStartsWhileAuthenticating, - sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - sm->auth_pae.authAuthReauthsWhileAuthenticated, - sm->auth_pae.authAuthEapStartsWhileAuthenticated, - sm->auth_pae.authAuthEapLogoffWhileAuthenticated, - sm->be_auth.backendResponses, - sm->be_auth.backendAccessChallenges, - sm->be_auth.backendOtherRequestsToSupplicant, - sm->be_auth.backendAuthSuccesses, - sm->be_auth.backendAuthFails); + ret = snprintf(buf + len, buflen - len, + "dot1xAuthEntersConnecting=%u\n" + "dot1xAuthEapLogoffsWhileConnecting=%u\n" + "dot1xAuthEntersAuthenticating=%u\n" + "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" + "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" + "dot1xAuthAuthFailWhileAuthenticating=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" + "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" + "dot1xAuthBackendResponses=%u\n" + "dot1xAuthBackendAccessChallenges=%u\n" + "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" + "dot1xAuthBackendAuthSuccesses=%u\n" + "dot1xAuthBackendAuthFails=%u\n", + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthSessionStatsTable */ - len += snprintf(buf + len, buflen - len, - /* TODO: dot1xAuthSessionOctetsRx */ - /* TODO: dot1xAuthSessionOctetsTx */ - /* TODO: dot1xAuthSessionFramesRx */ - /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" - "dot1xAuthSessionAuthenticMethod=%d\n" - "dot1xAuthSessionTime=%u\n" - "dot1xAuthSessionTerminateCause=999\n" - "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, - sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ? 1 : 2, - (unsigned int) (time(NULL) - sta->acct_session_start), - sm->identity); + ret = snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ + "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", + sta->acct_session_id_hi, sta->acct_session_id_lo, + wpa_auth_sta_key_mgmt(sta->wpa_sm) == + WPA_KEY_MGMT_IEEE8021X ? 1 : 2, + (unsigned int) (time(NULL) - sta->acct_session_start), + sm->identity); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } @@ -1784,7 +2000,11 @@ void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, static const int dot11RSNAConfigPMKLifetime = 43200; key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); + if (success && key && + wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry (IEEE 802.1X)"); } } diff --git a/contrib/hostapd/ieee802_1x.h b/contrib/hostapd/ieee802_1x.h index 90cc2ce..776d9df 100644 --- a/contrib/hostapd/ieee802_1x.h +++ b/contrib/hostapd/ieee802_1x.h @@ -1,26 +1,22 @@ /* $FreeBSD$ */ +/* + * hostapd / IEEE 802.1X Authenticator + * Copyright (c) 2002-2005, 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. + */ + #ifndef IEEE802_1X_H #define IEEE802_1X_H -/* IEEE Std 802.1X-REV-d11, 7.2 */ - -struct ieee802_1x_hdr { - u8 version; - u8 type; - u16 length; - /* followed by length octets of data */ -} __attribute__ ((packed)); - -#define EAPOL_VERSION 2 - -enum { IEEE802_1X_TYPE_EAP_PACKET = 0, - IEEE802_1X_TYPE_EAPOL_START = 1, - IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, - IEEE802_1X_TYPE_EAPOL_KEY = 3, - IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 -}; - /* draft-congdon-radius-8021x-20.txt */ struct ieee802_1x_eapol_key { @@ -45,32 +41,31 @@ struct ieee802_1x_eapol_key { * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ } __attribute__ ((packed)); -enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, - EAPOL_KEY_TYPE_WPA = 254 }; - void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); void ieee802_1x_request_identity(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, int success); -void ieee802_1x_tx_req(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_send_resp_to_server(struct hostapd_data *hapd, + struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, - int authorized); -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); -int ieee802_1x_init(hostapd *hapd); -void ieee802_1x_deinit(hostapd *hapd); -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); @@ -86,6 +81,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, void hostapd_get_ntp_timestamp(u8 *buf); void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success); +char *eap_type_text(u8 type); struct radius_class_data; diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c index ec07b35..7d40b9a 100644 --- a/contrib/hostapd/wpa.c +++ b/contrib/hostapd/wpa.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / WPA Authenticator - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, 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 @@ -15,40 +14,194 @@ * $FreeBSD$ */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS #include "hostapd.h" #include "eapol_sm.h" #include "wpa.h" -#include "driver.h" +#include "wme.h" #include "sha1.h" #include "md5.h" #include "rc4.h" #include "aes_wrap.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" +#include "crypto.h" #include "eloop.h" -#include "sta_info.h" -#include "l2_packet.h" -#include "accounting.h" -#include "hostap_common.h" +#include "ieee802_11.h" +#include "pmksa_cache.h" +#include "state_machine.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +#define RSN_NUM_REPLAY_COUNTERS_1 0 +#define RSN_NUM_REPLAY_COUNTERS_2 1 +#define RSN_NUM_REPLAY_COUNTERS_4 2 +#define RSN_NUM_REPLAY_COUNTERS_16 3 + + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[WPA_PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; + Boolean key_replay_counter_valid; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int sta_counted:1; + unsigned int mgmt_frame_prot:1; + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GNoStations; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; +#ifdef CONFIG_IEEE80211W + u8 DGTK[WPA_DGTK_LEN]; + u8 IGTK[2][WPA_IGTK_LEN]; +#endif /* CONFIG_IEEE80211W */ +}; + + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u8 dot11RSNAAuthenticationSuiteSelected[4]; + u8 dot11RSNAPairwiseCipherSelected[4]; + u8 dot11RSNAGroupCipherSelected[4]; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */ + u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */ + u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; +}; static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_sm_step(struct wpa_state_machine *sm); static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); -static void wpa_group_sm_step(struct hostapd_data *hapd); -static void pmksa_cache_free(struct hostapd_data *hapd); -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid); - +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); /* Default timeouts are 100 ms, but this seems to be a bit too fast for most * WPA Supplicants, so use a bit longer timeout. */ @@ -61,7 +214,6 @@ static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static const int pmksa_cache_max_entries = 1024; static const int WPA_SELECTOR_LEN = 4; @@ -75,6 +227,9 @@ static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; +#ifdef CONFIG_IEEE80211W +static const u8 RSN_CIPHER_SUITE_AES_128_CMAC[] = { 0x00, 0x0f, 0xac, 6 }; +#endif /* CONFIG_IEEE80211W */ static const int RSN_SELECTOR_LEN = 4; static const u16 RSN_VERSION = 1; @@ -88,12 +243,50 @@ static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; /* EAPOL-Key Key Data Encapsulation - * GroupKey and STAKey require encryption, otherwise, encryption is optional. + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. */ static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +#if 0 static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +#endif static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; +#ifdef CONFIG_PEERKEY +static const u8 RSN_KEY_DATA_SMK[] = { 0x00, 0x0f, 0xac, 5 }; +static const u8 RSN_KEY_DATA_NONCE[] = { 0x00, 0x0f, 0xac, 6 }; +static const u8 RSN_KEY_DATA_LIFETIME[] = { 0x00, 0x0f, 0xac, 7 }; +static const u8 RSN_KEY_DATA_ERROR[] = { 0x00, 0x0f, 0xac, 8 }; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +/* FIX: IEEE 802.11w/D1.0 is using subtypes 5 and 6 for these, but they were + * already taken by 802.11ma (PeerKey). Need to update the values here once + * IEEE 802.11w fixes these. */ +static const u8 RSN_KEY_DATA_DHV[] = { 0x00, 0x0f, 0xac, 9 }; +static const u8 RSN_KEY_DATA_IGTK[] = { 0x00, 0x0f, 0xac, 10 }; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_PEERKEY +enum { + STK_MUI_4WAY_STA_AP = 1, + STK_MUI_4WAY_STAT_STA = 2, + STK_MUI_GTK = 3, + STK_MUI_SMK = 4 +}; + +enum { + STK_ERR_STA_NR = 1, + STK_ERR_STA_NRSN = 2, + STK_ERR_CPHR_NS = 3, + STK_ERR_NO_STSL = 4 +}; +#endif /* CONFIG_PEERKEY */ + +#define GENERIC_INFO_ELEM 0xdd +#define RSN_INFO_ELEM 0x30 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ /* WPA IE version 1 * 00-50-f2:1 (OUI:OUI type) @@ -115,7 +308,7 @@ struct wpa_ie_hdr { u8 oui[3]; u8 oui_type; u16 version; -} __attribute__ ((packed)); +} STRUCT_PACKED; /* RSN IE version 1 @@ -131,16 +324,171 @@ struct wpa_ie_hdr { * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) */ struct rsn_ie_hdr { u8 elem_id; /* WLAN_EID_RSN */ u8 len; u16 version; -} __attribute__ ((packed)); +} STRUCT_PACKED; + + +struct rsn_error_kde { + u16 mui; + u16 error_type; +} STRUCT_PACKED; + + +#ifdef CONFIG_IEEE80211W +struct wpa_dhv_kde { + u8 dhv[WPA_DHV_LEN]; +} STRUCT_PACKED; + +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +static inline void wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *prev_psk) +{ + if (wpa_auth->cb.get_psk == NULL) + return NULL; + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); +} + + +static inline int wpa_auth_get_pmk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *pmk, size_t *len) +{ + if (wpa_auth->cb.get_pmk == NULL) + return -1; + return wpa_auth->cb.get_pmk(wpa_auth->cb.ctx, addr, pmk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +static inline int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, + void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +static void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +static void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, + const u8 *addr, logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = strlen(fmt) + 100; + format = malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + free(format); +} -static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; int num_suites; @@ -152,16 +500,17 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) hdr->version = host_to_le16(WPA_VERSION); pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += WPA_SELECTOR_LEN; @@ -170,25 +519,25 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -198,12 +547,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; @@ -211,8 +560,8 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); return -1; } *count++ = num_suites & 0xff; @@ -226,11 +575,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +static int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct rsn_ie_hdr *hdr; int num_suites; u8 *pos, *count; + u16 capab; hdr = (struct rsn_ie_hdr *) buf; hdr->elem_id = WLAN_EID_RSN; @@ -239,16 +589,17 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) *pos++ = RSN_VERSION >> 8; pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += RSN_SELECTOR_LEN; @@ -257,25 +608,25 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -285,12 +636,12 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; @@ -298,16 +649,43 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); return -1; } *count++ = num_suites & 0xff; *count = (num_suites >> 8) & 0xff; /* RSN Capabilities */ - *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0; - *pos++ = 0; + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wme_enabled) { + /* 4 PTKSA replay counters when using WME */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) + capab |= WPA_CAPABILITY_MGMT_FRAME_PROTECTION; +#endif /* CONFIG_IEEE80211W */ + *pos++ = capab & 0xff; + *pos++ = capab >> 8; + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (pos + 2 + 4 > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + + /* Management Group Cipher Suite */ + memcpy(pos, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ hdr->len = (pos - buf) - 2; @@ -315,443 +693,260 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_gen_wpa_ie(struct hostapd_data *hapd) +static int wpa_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { u8 *pos, buf[100]; int res; pos = buf; - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos); + if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA2) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) { - res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos); + if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } - free(hapd->wpa_ie); - hapd->wpa_ie = malloc(pos - buf); - if (hapd->wpa_ie == NULL) + free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) return -1; - memcpy(hapd->wpa_ie, buf, pos - buf); - hapd->wpa_ie_len = pos - buf; + memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; return 0; } -static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) { - hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; - if (hapd->wpa_auth) { - if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA " - "initialization.\n"); - } else { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "GMK rekeyd"); - } + if (hostapd_get_rand(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); } - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); } } static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; - if (hapd->wpa_auth) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); - hapd->wpa_auth->GTKReKey = TRUE; + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; do { - hapd->wpa_auth->changed = FALSE; - wpa_group_sm_step(hapd); - } while (hapd->wpa_auth->changed); - } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); - } -} - - -#ifdef CONFIG_RSN_PREAUTH - -static void rsn_preauth_receive(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface = ctx; - struct hostapd_data *hapd = piface->hapd; - struct ieee802_1x_hdr *hdr; - struct sta_info *sta; - struct l2_ethhdr *ethhdr; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " - "from interface '%s'\n", piface->ifname); - if (len < sizeof(*ethhdr) + sizeof(*hdr)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth " - "packet (len=%lu)\n", (unsigned long) len); - return; - } - - ethhdr = (struct l2_ethhdr *) buf; - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - - if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "foreign address " MACSTR "\n", - MAC2STR(ethhdr->h_dest)); - return; - } - - sta = ap_get_sta(hapd, ethhdr->h_source); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "already association STA " MACSTR "\n", - MAC2STR(sta->addr)); - return; - } - if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); - if (sta == NULL) - return; - memset(sta, 0, sizeof(*sta)); - memcpy(sta->addr, ethhdr->h_source, ETH_ALEN); - sta->flags = WLAN_STA_PREAUTH; - sta->next = hapd->sta_list; - sta->wpa = WPA_VERSION_WPA2; - hapd->sta_list = sta; - hapd->num_sta++; - ap_sta_hash_add(hapd, sta); - - ieee802_1x_new_station(hapd, sta); - if (sta->eapol_sm == NULL) { - ap_free_sta(hapd, sta); - sta = NULL; - } else { - sta->eapol_sm->radius_identifier = -1; - sta->eapol_sm->portValid = TRUE; - sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; - } - } - if (sta == NULL) - return; - sta->preauth_iface = piface; - ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), - len - sizeof(*ethhdr)); -} - - -static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) -{ - struct rsn_preauth_interface *piface; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", - ifname); - - piface = malloc(sizeof(*piface)); - if (piface == NULL) - return -1; - memset(piface, 0, sizeof(*piface)); - piface->hapd = hapd; - - piface->ifname = strdup(ifname); - if (piface->ifname == NULL) { - goto fail1; - } - - piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, - rsn_preauth_receive, piface, 1); - if (piface->l2 == NULL) { - printf("Failed to open register layer 2 access to " - "ETH_P_PREAUTH\n"); - goto fail2; - } - - piface->next = hapd->preauth_iface; - hapd->preauth_iface = piface; - return 0; - -fail2: - free(piface->ifname); -fail1: - free(piface); - return -1; -} - - -static void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ - struct rsn_preauth_interface *piface, *prev; - - piface = hapd->preauth_iface; - hapd->preauth_iface = NULL; - while (piface) { - prev = piface; - piface = piface->next; - l2_packet_deinit(prev->l2); - free(prev->ifname); - free(prev); - } -} - - -static int rsn_preauth_iface_init(struct hostapd_data *hapd) -{ - char *tmp, *start, *end; - - if (hapd->conf->rsn_preauth_interfaces == NULL) - return 0; - - tmp = strdup(hapd->conf->rsn_preauth_interfaces); - if (tmp == NULL) - return -1; - start = tmp; - for (;;) { - while (*start == ' ') - start++; - if (*start == '\0') - break; - end = strchr(start, ' '); - if (end) - *end = '\0'; - - if (rsn_preauth_iface_add(hapd, start)) { - rsn_preauth_iface_deinit(hapd); - return -1; - } - - if (end) - start = end + 1; - else - break; - } - free(tmp); - return 0; -} - - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " - MACSTR, MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - u8 *key; - size_t len; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pre-authentication %s", - success ? "succeeded" : "failed"); - - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); - } - - /* - * Finish STA entry removal from timeout in order to avoid freeing - * STA data before the caller has finished processing. - */ - eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface; - struct l2_ethhdr *ethhdr; - - piface = hapd->preauth_iface; - while (piface) { - if (piface == sta->preauth_iface) - break; - piface = piface->next; + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); } - if (piface == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " - "pre-authentication interface for " MACSTR "\n", - MAC2STR(sta->addr)); - return; + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); } - - ethhdr = malloc(sizeof(*ethhdr) + len); - if (ethhdr == NULL) - return; - - memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); - memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_PREAUTH); - memcpy(ethhdr + 1, buf, len); - - if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, - sizeof(*ethhdr) + len) < 0) { - printf("Failed to send preauth packet using l2_packet_send\n"); - } - free(ethhdr); } -#else /* CONFIG_RSN_PREAUTH */ -static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { + if (sm->pmksa == ctx) + sm->pmksa = NULL; return 0; } -static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ -} - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ -} - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ -} -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) { + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); } -#endif /* CONFIG_RSN_PREAUTH */ - -int wpa_init(struct hostapd_data *hapd) +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) { + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; u8 rkey[32]; - u8 buf[ETH_ALEN + 8]; - if (rsn_preauth_iface_init(hapd)) - return -1; + group = wpa_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; - if (hostapd_set_privacy(hapd, 1)) { - printf("Could not set PrivacyInvoked for interface %s\n", - hapd->conf->iface); - return -1; - } - - if (wpa_gen_wpa_ie(hapd)) { - printf("Could not generate WPA IE.\n"); - return -1; - } - - if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) { - printf("Failed to configure WPA IE for the kernel driver.\n"); - return -1; - } + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; - hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator)); - if (hapd->wpa_auth == NULL) - return -1; - memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator)); - hapd->wpa_auth->GTKAuthenticator = TRUE; - switch (hapd->conf->wpa_group) { + switch (wpa_auth->conf.wpa_group) { case WPA_CIPHER_CCMP: - hapd->wpa_auth->GTK_len = 16; + group->GTK_len = 16; break; case WPA_CIPHER_TKIP: - hapd->wpa_auth->GTK_len = 32; + group->GTK_len = 32; break; case WPA_CIPHER_WEP104: - hapd->wpa_auth->GTK_len = 13; + group->GTK_len = 13; break; case WPA_CIPHER_WEP40: - hapd->wpa_auth->GTK_len = 5; + group->GTK_len = 5; break; } /* Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ - memcpy(buf, hapd->own_addr, ETH_ALEN); - hostapd_get_ntp_timestamp(buf + ETH_ALEN); + memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); if (hostapd_get_rand(rkey, sizeof(rkey)) || - hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA initialization.\n"); - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; - return -1; + hostapd_get_rand(group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + free(group); + return NULL; } sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - hapd->wpa_auth->Counter, WPA_NONCE_LEN); + group->Counter, WPA_NONCE_LEN); - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = wpa_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + memcpy(wpa_auth->addr, addr, ETH_ALEN); + memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + free(wpa_auth); + return NULL; } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); + wpa_auth->group = wpa_group_init(wpa_auth, 0); + if (wpa_auth->group == NULL) { + free(wpa_auth->wpa_ie); + free(wpa_auth); + return NULL; } - hapd->wpa_auth->GInit = TRUE; - wpa_group_sm_step(hapd); - hapd->wpa_auth->GInit = FALSE; - wpa_group_sm_step(hapd); + wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + free(wpa_auth->wpa_ie); + free(wpa_auth); + return NULL; + } - return 0; + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; } -void wpa_deinit(struct hostapd_data *hapd) +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) { - rsn_preauth_iface_deinit(hapd); + struct wpa_group *group, *prev; - eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - if (hostapd_set_privacy(hapd, 0)) { - printf("Could not disable PrivacyInvoked for interface %s\n", - hapd->conf->iface); - } + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); + + pmksa_cache_deinit(wpa_auth->pmksa); + + free(wpa_auth->wpa_ie); - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { - printf("Could not remove generic information element from " - "interface %s\n", hapd->conf->iface); + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + free(prev); } - free(hapd->wpa_ie); - hapd->wpa_ie = NULL; - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; + free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + if (wpa_auth == NULL) + return 0; - pmksa_cache_free(hapd); + memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + /* + * TODO: + * Disassociate stations if configuration changed + * Update WPA/RSN IE + */ + return 0; } @@ -794,6 +989,10 @@ static int rsn_selector_to_bitfield(u8 *s) return WPA_CIPHER_CCMP; if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) return WPA_CIPHER_WEP104; +#ifdef CONFIG_IEEE80211W + if (memcmp(s, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ return 0; } @@ -809,263 +1008,20 @@ static int rsn_key_mgmt_to_bitfield(u8 *s) } -static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - - hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); - memcpy(pmkid, hash, PMKID_LEN); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd); - - -static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) -{ - if (entry == NULL) - return; - free(entry->identity); - ieee802_1x_free_radius_class(&entry->radius_class); - free(entry); -} - - -static void pmksa_cache_free_entry(struct hostapd_data *hapd, - struct rsn_pmksa_cache *entry) -{ - struct sta_info *sta; - struct rsn_pmksa_cache *pos, *prev; - hapd->pmksa_count--; - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - if (sta->pmksa == entry) - sta->pmksa = NULL; - } - pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - hapd->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } - break; - } - prev = pos; - pos = pos->hnext; - } - - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) - prev->next = pos->next; - else - hapd->pmksa = pos->next; - break; - } - prev = pos; - pos = pos->next; - } - _pmksa_cache_free_entry(entry); -} - - -static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - time_t now; - - time(&now); - while (hapd->pmksa && hapd->pmksa->expiration <= now) { - struct rsn_pmksa_cache *entry = hapd->pmksa; - hapd->pmksa = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(hapd, entry); - } - - pmksa_cache_set_expiration(hapd); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd) -{ - int sec; - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - if (hapd->pmksa == NULL) - return; - sec = hapd->pmksa->expiration - time(NULL); - if (sec < 0) - sec = 0; - eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); -} - - -static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (eapol == NULL) - return; - - if (eapol->identity) { - entry->identity = malloc(eapol->identity_len); - if (entry->identity) { - entry->identity_len = eapol->identity_len; - memcpy(entry->identity, eapol->identity, - eapol->identity_len); - } - } - - ieee802_1x_copy_radius_class(&entry->radius_class, - &eapol->radius_class); -} - - -static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (entry == NULL || eapol == NULL) - return; - - if (entry->identity) { - free(eapol->identity); - eapol->identity = malloc(entry->identity_len); - if (eapol->identity) { - eapol->identity_len = entry->identity_len; - memcpy(eapol->identity, entry->identity, - entry->identity_len); - } - wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", - eapol->identity, eapol->identity_len); - } - - ieee802_1x_free_radius_class(&eapol->radius_class); - ieee802_1x_copy_radius_class(&eapol->radius_class, - &entry->radius_class); - if (eapol->radius_class.attr) { - wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " - "PMKSA", (unsigned long) eapol->radius_class.count); - } -} - - -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout) -{ - struct rsn_pmksa_cache *entry, *pos, *prev; - - if (sta->wpa != WPA_VERSION_WPA2) - return; - - entry = malloc(sizeof(*entry)); - if (entry == NULL) - return; - memset(entry, 0, sizeof(*entry)); - memcpy(entry->pmk, pmk, PMK_LEN); - rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); - time(&entry->expiration); - if (session_timeout > 0) - entry->expiration += session_timeout; - else - entry->expiration += dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->spa, sta->addr, ETH_ALEN); - pmksa_cache_from_eapol_data(entry, sta->eapol_sm); - - /* Replace an old entry for the same STA (if found) with the new entry - */ - pos = pmksa_cache_get(hapd, sta->addr, NULL); - if (pos) - pmksa_cache_free_entry(hapd, pos); - - if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { - /* Remove the oldest entry to make room for the new entry */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: removed the oldest PMKSA cache entry (for " - MACSTR ") to make room for new one", - MAC2STR(hapd->pmksa->spa)); - pmksa_cache_free_entry(hapd, hapd->pmksa); - } - - /* Add the new entry; order by expiration time */ - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = hapd->pmksa; - hapd->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; - } - entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; - - hapd->pmksa_count++; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "added PMKSA cache entry"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN); - } -} - - -static void pmksa_cache_free(struct hostapd_data *hapd) +static u8 * wpa_add_kde(u8 *pos, const u8 *kde, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len) { - struct rsn_pmksa_cache *entry, *prev; - int i; - struct sta_info *sta; - - entry = hapd->pmksa; - hapd->pmksa = NULL; - while (entry) { - prev = entry; - entry = entry->next; - _pmksa_cache_free_entry(prev); - } - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - for (i = 0; i < PMKID_HASH_SIZE; i++) - hapd->pmkid[i] = NULL; - for (sta = hapd->sta_list; sta; sta = sta->next) - sta->pmksa = NULL; -} - - -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid) -{ - struct rsn_pmksa_cache *entry; - - if (pmkid) - entry = hapd->pmkid[PMKID_HASH(pmkid)]; - else - entry = hapd->pmksa; - while (entry) { - if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; + *pos++ = GENERIC_INFO_ELEM; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + memcpy(pos, kde, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + memcpy(pos, data2, data2_len); + pos += data2_len; } - return NULL; + return pos; } @@ -1076,6 +1032,7 @@ struct wpa_ie_data { int capabilities; size_t num_pmkid; u8 *pmkid; + int mgmt_group_cipher; }; @@ -1091,6 +1048,7 @@ static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, data->pairwise_cipher = WPA_CIPHER_TKIP; data->group_cipher = WPA_CIPHER_TKIP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->mgmt_group_cipher = 0; if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) return -1; @@ -1170,6 +1128,11 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, data->pairwise_cipher = WPA_CIPHER_CCMP; data->group_cipher = WPA_CIPHER_CCMP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_IEEE80211W + data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; +#else /* CONFIG_IEEE80211W */ + data->mgmt_group_cipher = 0; +#endif /* CONFIG_IEEE80211W */ if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) return -1; @@ -1232,10 +1195,10 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, data->num_pmkid = pos[0] | (pos[1] << 8); pos += 2; left -= 2; - if (left < data->num_pmkid * PMKID_LEN) { - printf("RSN: too short RSN IE for PMKIDs " - "(num=%lu, left=%d)\n", - (unsigned long) data->num_pmkid, left); + if (left < (int) data->num_pmkid * PMKID_LEN) { + wpa_printf(MSG_DEBUG, "RSN: too short RSN IE for " + "PMKIDs (num=%lu, left=%d)", + (unsigned long) data->num_pmkid, left); return -9; } data->pmkid = pos; @@ -1243,6 +1206,20 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, left -= data->num_pmkid * PMKID_LEN; } +#ifdef CONFIG_IEEE80211W + if (left >= 4) { + data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); + if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "RSN: Unsupported management " + "group cipher 0x%x", + data->mgmt_group_cipher); + return -10; + } + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + if (left > 0) { return -8; } @@ -1251,12 +1228,25 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version) +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len) { struct wpa_ie_data data; - int ciphers, key_mgmt, res, i; + int ciphers, key_mgmt, res, version; const u8 *selector; + size_t i; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = HOSTAPD_WPA_VERSION_WPA2; + else + version = HOSTAPD_WPA_VERSION_WPA; if (version == HOSTAPD_WPA_VERSION_WPA2) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); @@ -1266,7 +1256,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1280,7 +1270,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1294,7 +1284,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, RSN_SELECTOR_LEN); } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); @@ -1304,7 +1294,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1318,7 +1308,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1332,99 +1322,291 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, WPA_SELECTOR_LEN); } if (res) { - printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n", - MAC2STR(sta->addr), res); - hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len); + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); return WPA_INVALID_IE; } - if (data.group_cipher != hapd->conf->wpa_group) { - printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n", - data.group_cipher, MAC2STR(sta->addr)); + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); return WPA_INVALID_GROUP; } - key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt; + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; if (!key_mgmt) { - printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", - data.key_mgmt, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); return WPA_INVALID_AKMP; } if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) - sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else - sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; if (!ciphers) { - printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", - data.pairwise_cipher, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Invalid WPA pairwise cipher (0x%x) " + "from " MACSTR, + data.pairwise_cipher, MAC2STR(sm->addr)); return WPA_INVALID_PAIRWISE; } +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { + if (!(data.capabilities & + WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || + !(data.capabilities & WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + if (ciphers & WPA_CIPHER_CCMP) - sta->pairwise = WPA_CIPHER_CCMP; + sm->pairwise = WPA_CIPHER_CCMP; else - sta->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = WPA_CIPHER_TKIP; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) - sta->wpa = WPA_VERSION_WPA2; + sm->wpa = WPA_VERSION_WPA2; else - sta->wpa = WPA_VERSION_WPA; + sm->wpa = WPA_VERSION_WPA; for (i = 0; i < data.num_pmkid; i++) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN IE: STA PMKID", - &data.pmkid[i * PMKID_LEN], PMKID_LEN); - } - sta->pmksa = pmksa_cache_get(hapd, sta->addr, - &data.pmkid[i * PMKID_LEN]); - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "PMKID found from PMKSA cache"); - if (hapd->wpa_auth) { - memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed, - sta->pmksa->pmkid, PMKID_LEN); - } + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + memcpy(wpa_auth->dot11RSNAPMKIDUsed, + sm->pmksa->pmkid, PMKID_LEN); break; } } + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + free(sm->wpa_ie); + sm->wpa_ie = malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + return WPA_IE_OK; } -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +}; + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + memcmp(pos + 2, RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN) == 0) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN) == 0) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_MAC_ADDR, RSN_SELECTOR_LEN) == 0) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_SMK, RSN_SELECTOR_LEN) == 0) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_NONCE, RSN_SELECTOR_LEN) == 0) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_LIFETIME, RSN_SELECTOR_LEN) == 0) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_ERROR, RSN_SELECTOR_LEN) == 0) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +static int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d)", pos[0], pos[1]); + ret = -1; + break; + } + if (*pos == RSN_INFO_ELEM) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; + } else if (*pos == GENERIC_INFO_ELEM) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) { struct wpa_state_machine *sm; - if (!hapd->conf->wpa) + sm = wpa_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + + return sm; +} + + +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; - if (sta->wpa_sm) { - sm = sta->wpa_sm; + if (sm->started) { memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->ReAuthenticationRequest = TRUE; wpa_sm_step(sm); return; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sm = malloc(sizeof(struct wpa_state_machine)); - if (sm == NULL) - return; - memset(sm, 0, sizeof(struct wpa_state_machine)); - - sm->hapd = hapd; - sm->sta = sta; - sta->wpa_sm = sm; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; sm->Init = TRUE; wpa_sm_step(sm); @@ -1434,36 +1616,43 @@ void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void wpa_free_station(struct sta_info *sta) +static void wpa_free_sta_sm(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_sm; + free(sm->last_rx_eapol_key); + free(sm->wpa_ie); + free(sm); +} + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ if (sm == NULL) return; - if (sm->hapd->conf->wpa_strict_rekey && sm->has_GTK) { - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "strict rekeying - force " - "GTK rekey since STA is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd, + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, NULL); } - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); - eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); - eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); - free(sm->last_rx_eapol_key); - free(sm); - sta->wpa_sm = NULL; + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); } -static void wpa_request_new_ptk(struct hostapd_data *hapd, - struct sta_info *sta) +static void wpa_request_new_ptk(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_sm; - if (sm == NULL) return; @@ -1472,20 +1661,400 @@ static void wpa_request_new_ptk(struct hostapd_data *hapd, } -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, +#ifdef CONFIG_PEERKEY +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + size_t kde_len; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + kde_len = 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde); + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, kde_len, 0, 0, 0); +} + + +static void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, buf_len, 0, 0, 0); + + free(buf); + +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, buf_len, 0, 1, 0); + + free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, buf_len, 0, 1, 0); + + free(buf); +} + + +static void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (hostapd_get_rand(smk, WPA_PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + memcpy(pos, key->key_nonce, WPA_NONCE_LEN); + sha1_prf(smk, WPA_PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, WPA_PMK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, WPA_PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + memset(smk, 0, sizeof(*smk)); +} + + +static void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} +#endif /* CONFIG_PEERKEY */ + + +static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ +#ifdef CONFIG_PEERKEY + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } +#endif /* CONFIG_PEERKEY */ + + return -1; +} + + +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, u8 *data, size_t data_len) { - struct wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; + struct wpa_eapol_ie_parse kde; - if (!hapd->conf->wpa) - return; - - if (sm == NULL) + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1507,7 +2076,19 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ - if (key_info & WPA_KEY_INFO_REQUEST) { + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { @@ -1522,13 +2103,12 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } if (key_info & WPA_KEY_INFO_REQUEST) { - if (sta->req_replay_counter_used && - memcmp(key->replay_counter, sta->req_replay_counter, + if (sm->req_replay_counter_used && + memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_WARNING, - "received EAPOL-Key request with " - "replayed counter"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); return; } } @@ -1537,18 +2117,13 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, (!sm->key_replay_counter_valid || memcmp(key->replay_counter, sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN) != 0)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key %s with unexpected replay " - "counter", msgtxt); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("expected replay counter", - sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - hostapd_hexdump("received replay counter", - key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key %s with unexpected " + "replay counter", msgtxt); + wpa_hexdump(MSG_DEBUG, "expected replay counter", + sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); return; } @@ -1556,142 +2131,170 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); return; } - if (sta->wpa_ie == NULL || - sta->wpa_ie_len != key_data_length || - memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "WPA IE from (Re)AssocReq did not match" - " with msg 2/4"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - if (sta->wpa_ie) { - hostapd_hexdump("WPA IE in AssocReq", - sta->wpa_ie, - sta->wpa_ie_len); - } - hostapd_hexdump("WPA IE in msg 2/4", - (u8 *) (key + 1), - key_data_length); + if (sm->wpa_ie == NULL || + sm->wpa_ie_len != key_data_length || + memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + (u8 *) (key + 1), key_data_length); /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(hapd, sta); + wpa_sta_disconnect(wpa_auth, sm->addr); return; } break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 4/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); return; } break; case GROUP_2: if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/2 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_group_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); return; } break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ case REQUEST: break; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)", - msgtxt); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key Ack set"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key MIC not set"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); return; } sm->MICVerified = FALSE; if (sm->PTK_valid) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key with invalid MIC"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); return; } sm->MICVerified = TRUE; - eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd, - sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { - sta->req_replay_counter_used = 1; - memcpy(sta->req_replay_counter, key->replay_counter, + sm->req_replay_counter_used = 1; + memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key request with " - "invalid MIC"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); return; } - if (key_info & WPA_KEY_INFO_ERROR) { + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { /* Supplicant reported a Michael MIC error */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - ieee80211_michael_mic_failure(hapd, sta->addr, 0); - sta->dot11RSNAStatsTKIPRemoteMICFailures++; - hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; /* Error report is not a request for a new key * handshake, but since Authenticator may do it, let's * change the keys now anyway. */ - wpa_request_new_ptk(hapd, sta); + wpa_request_new_ptk(sm); } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for new " - "4-Way Handshake"); - wpa_request_new_ptk(hapd, sta); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { } else { - /* TODO: this could also be a request for STAKey - * if Key Data fields contains peer MAC address KDE. - * STAKey request should have 0xdd <len> 00-0F-AC:2 in - * the beginning of Key Data */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for GTK " - "rekeying"); - wpa_request_new_ptk(hapd, sta); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); - wpa_rekey_gtk(hapd, NULL); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + /* FIX: why was this triggering PTK rekeying for the + * STA that requested Group Key rekeying?? */ + /* wpa_request_new_ptk(sta->wpa_sm); */ + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); } } else { /* Do not allow the same key replay counter to be reused. */ sm->key_replay_counter_valid = FALSE; } +#ifdef CONFIG_PEERKEY + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + free(sm->last_rx_eapol_key); sm->last_rx_eapol_key = malloc(data_len); if (sm->last_rx_eapol_key == NULL) @@ -1707,8 +2310,7 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } -static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, - const u8 *addr1, const u8 *addr2, +static void wpa_pmk_to_ptk(const u8 *pmk, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, u8 *ptk, size_t ptk_len) { @@ -1739,15 +2341,13 @@ static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", data, sizeof(data), ptk, ptk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("PMK", pmk, WPA_PMK_LEN); - hostapd_hexdump("PTK", ptk, ptk_len); - } + wpa_hexdump_key(MSG_DEBUG, "PMK", pmk, WPA_PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PTK", ptk, ptk_len); } -static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, - u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len) +static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, + u8 *gtk, size_t gtk_len) { u8 data[ETH_ALEN + WPA_NONCE_LEN]; @@ -1758,25 +2358,19 @@ static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", data, sizeof(data), gtk, gtk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("GMK", gmk, WPA_GMK_LEN); - hostapd_hexdump("GTK", gtk, gtk_len); - } + wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); } static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - - if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC)) - return; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout"); - sta->wpa_sm->TimeoutEvt = TRUE; - wpa_sm_step(sta->wpa_sm); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); } @@ -1800,88 +2394,68 @@ static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, } -static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, - int secure, int mic, int ack, int install, - int pairwise, u8 *key_rsc, u8 *nonce, - u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len, - int keyidx) +static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) { - struct wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; size_t len; - int key_info, alg; - int timeout_ms; + int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; - - if (sm == NULL) - return; + int version, pairwise; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); - if (sta->wpa == WPA_VERSION_WPA2) { - key_data_len = ie_len + gtk_len; - if (gtk_len) - key_data_len += 2 + RSN_SELECTOR_LEN + 2; - } else { - if (pairwise) { - /* WPA does not include GTK in 4-Way Handshake */ - gtk = NULL; - gtk_len = 0; - - /* key_rsc is for group key, so mask it out in case of - * WPA Pairwise key negotiation. */ - key_rsc = NULL; - } - key_data_len = ie_len + gtk_len; - } + if (force_version) + version = force_version; + else if (sm->pairwise == WPA_CIPHER_CCMP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - if (sta->pairwise == WPA_CIPHER_CCMP) { - key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - if (gtk) { - pad_len = key_data_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - key_data_len += pad_len + 8; - } - } else { - key_info = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(secure=%d mic=%d ack=%d " + "install=%d pairwise=%d kde_len=%lu keyidx=%d encr=%d)", + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; } len += key_data_len; - hdr = malloc(len); + hdr = wpa_zalloc(len); if (hdr == NULL) return; - memset(hdr, 0, len); - hdr->version = EAPOL_VERSION; + hdr->version = wpa_auth->conf.eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = htons(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); - key->type = sta->wpa == WPA_VERSION_WPA2 ? + key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - if (secure) - key_info |= WPA_KEY_INFO_SECURE; - if (mic) - key_info |= WPA_KEY_INFO_MIC; - if (ack) - key_info |= WPA_KEY_INFO_ACK; - if (install) - key_info |= WPA_KEY_INFO_INSTALL; - if (pairwise) - key_info |= WPA_KEY_INFO_KEY_TYPE; - if (gtk && sta->wpa == WPA_VERSION_WPA2) + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; - if (sta->wpa != WPA_VERSION_WPA2) { - if (pairwise) - keyidx = 0; + if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; - } key->key_info = htons(key_info); - alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; switch (alg) { case WPA_CIPHER_CCMP: key->key_length = htons(16); @@ -1896,7 +2470,10 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, key->key_length = htons(13); break; } + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + key->key_length = htons(0); + /* FIX: STSL: what to use as key_replay_counter? */ inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); memcpy(key->replay_counter, sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); @@ -1908,48 +2485,33 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, if (key_rsc) memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); - if (ie && !gtk) { - memcpy(key + 1, ie, ie_len); - key->key_data_length = htons(ie_len); - } else if (gtk) { - buf = malloc(key_data_len); + if (kde && !encr) { + memcpy(key + 1, kde, kde_len); + key->key_data_length = htons(kde_len); + } else if (encr && kde) { + buf = wpa_zalloc(key_data_len); if (buf == NULL) { free(hdr); return; } - memset(buf, 0, key_data_len); pos = buf; - if (ie) { - memcpy(pos, ie, ie_len); - pos += ie_len; - } - if (sta->wpa == WPA_VERSION_WPA2) { - *pos++ = WLAN_EID_GENERIC; - *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len; - memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - *pos++ = keyidx & 0x03; - *pos++ = 0; - } - memcpy(pos, gtk, gtk_len); - pos += gtk_len; + memcpy(pos, kde, kde_len); + pos += kde_len; + if (pad_len) *pos++ = 0xdd; - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("Plaintext EAPOL-Key Key Data", - buf, key_data_len); - } - if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf, (u8 *) (key + 1)); key->key_data_length = htons(key_data_len); } else { u8 ek[32]; memcpy(key->key_iv, - hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16, - 16); - inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN); + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); memcpy(ek, key->key_iv, 16); memcpy(ek + 16, sm->PTK.encr_key, 16); memcpy(key + 1, buf, key_data_len); @@ -1959,28 +2521,46 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, free(buf); } - if (mic) { + if (key_info & WPA_KEY_INFO_MIC) { if (!sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "PTK not valid " - "when sending EAPOL-Key frame"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); free(hdr); return; } - wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, + wpa_calc_eapol_key_mic(version, sm->PTK.mic_key, (u8 *) hdr, len, key->key_mic); } - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : dot11RSNAConfigGroupUpdateTimeOut; eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, - wpa_send_eapol_timeout, hapd, sta); + wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1989,7 +2569,7 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info; - int type, ret = 0; + int ret = 0; u8 mic[16]; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1998,7 +2578,6 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = ntohs(key->key_info); - type = key_info & WPA_KEY_INFO_TYPE_MASK; memcpy(mic, key->key_mic, 16); memset(key->key_mic, 0, 16); if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, @@ -2010,16 +2589,23 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) } -void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event) +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); + sm->pairwise_set = FALSE; +} + + +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) { - struct wpa_state_machine *sm = sta->wpa_sm; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "event %d notification", event); if (sm == NULL) return; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + switch (event) { case WPA_AUTH: case WPA_ASSOC: @@ -2037,11 +2623,8 @@ void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, sm->PTK_valid = FALSE; memset(&sm->PTK, 0, sizeof(sm->PTK)); - if (event != WPA_REAUTH_EAPOL) { - sm->pairwise_set = FALSE; - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, - (u8 *) "", 0); - } + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); wpa_sm_step(sm); } @@ -2063,31 +2646,9 @@ static const char * wpa_alg_txt(int alg) } -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->changed = TRUE; \ -sm->_data ## _ ## state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("WPA: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->sta->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct wpa_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - SM_STATE(WPA_PTK, INITIALIZE) { - struct hostapd_data *hapd = sm->hapd; - - SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ @@ -2096,64 +2657,75 @@ SM_STATE(WPA_PTK, INITIALIZE) sm->keycount = 0; if (sm->GUpdateStationKeys) - hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; - if (sm->sta->wpa == WPA_VERSION_WPA) + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and * Local AA > Remote AA)) */) { sm->Pair = TRUE; } - ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0); - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "", - 0); - sm->pairwise_set = FALSE; - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } } SM_STATE(WPA_PTK, DISCONNECT) { - SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->hapd, sm->sta); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { - SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); - sm->hapd->wpa_auth->GNoStations--; + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + if (sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "decrease GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { - SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); - sm->hapd->wpa_auth->GNoStations++; + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + if (!sm->sta_counted) { + sm->group->GNoStations++; + sm->sta_counted = 1; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "increase GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->portControl = Auto; - sm->sta->eapol_sm->portEnabled = TRUE; - } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); sm->AuthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION2) { - SM_ENTRY(WPA_PTK, AUTHENTICATION2, wpa_ptk); - memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; - /* IEEE 802.11i/D9.0 does not clear TimeoutCtr here, but this is more + /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through * INITIALIZE. */ @@ -2163,24 +2735,22 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) SM_STATE(WPA_PTK, INITPMK) { - u8 *key; - size_t len; - SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); - if (sm->sta->pmksa) { + size_t len = WPA_PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); + if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); - pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); - } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { + memcpy(sm->PMK, sm->pmksa->pmk, WPA_PMK_LEN); + } else if (wpa_auth_get_pmk(sm->wpa_auth, sm->addr, sm->PMK, &len) == + 0) { wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(len=%lu)", (unsigned long) len); - if (len > WPA_PMK_LEN) - len = WPA_PMK_LEN; - memcpy(sm->PMK, key, len); } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); } - sm->sta->req_replay_counter_used = 0; - /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this * will break reauthentication since EAPOL state machines may not be * get into AUTHENTICATING state that clears keyRun before WPA state * machine enters AUTHENTICATION2 state and goes immediately to INITPMK @@ -2188,45 +2758,57 @@ SM_STATE(WPA_PTK, INITPMK) * eventually fail in 4-Way Handshake because Supplicant uses PMK * derived from the new AAA Key. Setting keyRun = FALSE here seems to * be good workaround for this issue. */ - if (sm->sta->eapol_sm) - sm->sta->eapol_sm->keyRun = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; - SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk); - psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL); + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); if (psk) memcpy(sm->PMK, psk, WPA_PMK_LEN); - sm->sta->req_replay_counter_used = 0; + sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { - u8 *pmkid = NULL; + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; - SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/4 msg of 4-Way Handshake"); - if (sm->sta->pmksa && - (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) { + pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_GENERIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN); - memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid, - PMKID_LEN); + if (sm->pmksa) + memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, + PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, WPA_PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN]); + } } - wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, - pmkid, pmkid_len, NULL, 0, 0); - free(pmkid); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); sm->TimeoutCtr++; } @@ -2237,23 +2819,22 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) int ok = 0; const u8 *pmk = NULL; - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, - pmk); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; - wpa_pmk_to_ptk(sm->hapd, pmk, sm->hapd->own_addr, - sm->sta->addr, sm->ANonce, sm->SNonce, + wpa_pmk_to_ptk(pmk, sm->wpa_auth->addr, sm->addr, + sm->ANonce, sm->SNonce, (u8 *) &PTK, sizeof(PTK)); if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, @@ -2262,20 +2843,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) break; } - if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK) + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) break; } if (!ok) { - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 " - "of 4-Way Handshake"); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); return; } - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { /* PSK may have changed from the previous choice, so update * state machine data based on whatever PSK was selected here. */ @@ -2291,99 +2871,199 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); sm->TimeoutCtr = 0; } +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_dhv_kde) + + 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_dhv_kde dhv; + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + u8 mac[32]; + const u8 *addr[3]; + size_t len[3]; + + if (!sm->mgmt_frame_prot) + return pos; + + addr[0] = sm->wpa_auth->addr; + len[0] = ETH_ALEN; + addr[1] = sm->addr; + len[1] = ETH_ALEN; + addr[2] = gsm->DGTK; + len[2] = WPA_DGTK_LEN; + sha256_vector(3, addr, len, mac); + memcpy(dhv.dhv, mac, WPA_DHV_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPA: DHV", dhv.dhv, WPA_DHV_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_DHV, + (const u8 *) &dhv, sizeof(dhv), NULL, 0); + + igtk.keyid[0] = gsm->GN; + igtk.keyid[1] = 0; + if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN, igtk.pn) < 0) + memset(igtk.pn, 0, sizeof(igtk.pn)); + memcpy(igtk.igtk, gsm->IGTK[gsm->GN - 1], WPA_IGTK_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8 *) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; u8 *wpa_ie; - int wpa_ie_len; + int wpa_ie_len, secure, keyidx, encr = 0; - SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) */ memset(rsc, 0, WPA_KEY_RSC_LEN); - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - wpa_ie = sm->hapd->wpa_ie; - wpa_ie_len = sm->hapd->wpa_ie_len; - if (sm->sta->wpa == WPA_VERSION_WPA && - (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA2) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 3/4 msg of 4-Way Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, - sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0, - 1, 1, 1, 1, rsc, sm->ANonce, - wpa_ie, wpa_ie_len, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; + kde = malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + free(kde); sm->TimeoutCtr++; } SM_STATE(WPA_PTK, PTKINITDONE) { - SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { char *alg; int klen; - if (sm->sta->pairwise == WPA_CIPHER_TKIP) { + if (sm->pairwise == WPA_CIPHER_TKIP) { alg = "TKIP"; klen = 32; } else { alg = "CCMP"; klen = 16; } - if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0, - sm->PTK.tk1, klen)) { - wpa_sta_disconnect(sm->hapd, sm->sta); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } } if (0 /* IBSS == TRUE */) { sm->keycount++; if (sm->keycount == 2) { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); } } else { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); - } - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->keyAvailable = FALSE; - sm->sta->eapol_sm->keyDone = TRUE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); } - if (sm->sta->wpa == WPA_VERSION_WPA) + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = TRUE; else sm->has_GTK = TRUE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - accounting_sta_start(sm->hapd, sm->sta); + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); } SM_STEP(WPA_PTK) { - struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); @@ -2411,15 +3091,17 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: - if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && - sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun) + if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); - else if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); break; case WPA_PTK_INITPMK: - if (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable) + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2427,13 +3109,11 @@ SM_STEP(WPA_PTK) } break; case WPA_PTK_INITPSK: - if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { - hostapd_logger(sm->hapd, sm->sta->addr, - HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "no PSK configured for the STA"); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } @@ -2442,7 +3122,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2464,7 +3145,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2478,7 +3160,7 @@ SM_STEP(WPA_PTK) SM_STATE(WPA_PTK_GROUP, IDLE) { - SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ @@ -2491,46 +3173,69 @@ SM_STATE(WPA_PTK_GROUP, IDLE) SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; - SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); - if (sm->sta->wpa == WPA_VERSION_WPA) + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ memset(rsc, 0, WPA_KEY_RSC_LEN); if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/2 msg of Group Key Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc, - gsm->GNonce, NULL, 0, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gsm->GTK[gsm->GN - 1]; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + free(kde); sm->GTimeoutCtr++; } SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { - SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; sm->GUpdateStationKeys = FALSE; - sm->hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "group key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); sm->has_GTK = TRUE; } SM_STATE(WPA_PTK_GROUP, KEYERROR) { - SM_ENTRY(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); - sm->hapd->wpa_auth->GKeyDoneStations--; + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } @@ -2543,14 +3248,15 @@ SM_STEP(WPA_PTK_GROUP) else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || - (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_REKEYNEGOTIATING: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); - else if (sm->GTimeoutCtr > dot11RSNAConfigGroupUpdateCount) + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2565,159 +3271,185 @@ SM_STEP(WPA_PTK_GROUP) } -static void wpa_group_gtk_init(struct hostapd_data *hapd) +static void wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state GTK_INIT\n"); - sm->changed = FALSE; /* GInit is not cleared here; avoid loop */ - sm->wpa_group_state = WPA_GROUP_GTK_INIT; + /* FIX: is this the correct way of getting GNonce? */ + memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + hostapd_get_rand(group->DGTK, WPA_DGTK_LEN); + wpa_hexdump_key(MSG_DEBUG, "DGTK", group->DGTK, WPA_DGTK_LEN); + hostapd_get_rand(group->IGTK[group->GN - 1], WPA_IGTK_LEN); + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN - 1], WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; /* GTK[0..N] = 0 */ - memset(sm->GTK, 0, sizeof(sm->GTK)); - sm->GN = 1; - sm->GM = 2; + memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; /* GTK[GN] = CalcGTK() */ - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_gtk_update(wpa_auth, group); } -static void wpa_group_setkeys(struct hostapd_data *hapd) +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { - struct wpa_authenticator *sm = hapd->wpa_auth; - struct sta_info *sta; - int tmp; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYS\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYS; - sm->GTKReKey = FALSE; - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - sm->GKeyDoneStations = sm->GNoStations; - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); - - sta = hapd->sta_list; - while (sta) { - if (sta->wpa_sm) { - sta->wpa_sm->GUpdateStationKeys = TRUE; - wpa_sm_step(sta->wpa_sm); - } - sta = sta->next; - } + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); + return 0; } -static void wpa_group_setkeysdone(struct hostapd_data *hapd) +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; + group->GKeyDoneStations = group->GNoStations; + wpa_gtk_update(wpa_auth, group); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYSDONE\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYSDONE; - hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group), - NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); } -static void wpa_group_sm_step(struct hostapd_data *hapd) +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_alg_txt(wpa_auth->conf.wpa_group), + NULL, group->GN, group->GTK[group->GN - 1], + group->GTK_len); - if (sm->GInit) { - wpa_group_gtk_init(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT && - sm->GTKAuthenticator) { - wpa_group_setkeysdone(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE && - sm->GTKReKey) { - wpa_group_setkeys(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) { - if (sm->GKeyDoneStations == 0) - wpa_group_setkeysdone(hapd); - else if (sm->GTKReKey) - wpa_group_setkeys(hapd); +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", + NULL, group->GN, group->IGTK[group->GN - 1], + WPA_IGTK_LEN); + wpa_auth_set_key(wpa_auth, group->vlan_id, "DGTK", + NULL, 0, group->DGTK, WPA_DGTK_LEN); } +#endif /* CONFIG_IEEE80211W */ } -static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->wpa_sm == NULL) - return 0; - return 1; + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } } static void wpa_sm_step(struct wpa_state_machine *sm) { - struct hostapd_data *hapd = sm->hapd; - u8 addr[6]; - if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL) + if (sm == NULL) + return; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); return; + } - memcpy(addr, sm->sta->addr, 6); + sm->in_step_loop = 1; do { + if (sm->pending_deinit) + break; + sm->changed = FALSE; - sm->hapd->wpa_auth->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - wpa_group_sm_step(sm->hapd); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; - } while (sm->changed || sm->hapd->wpa_auth->changed); + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + } } static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) { - struct wpa_state_machine *sm = timeout_ctx; + struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) +void wpa_auth_sm_notify(struct wpa_state_machine *sm) { - if (sta->wpa_sm == NULL) + if (sm == NULL) return; - eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } -void wpa_gtk_rekey(struct hostapd_data *hapd) +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { - struct wpa_authenticator *sm = hapd->wpa_auth; int tmp, i; + struct wpa_group *group; - if (sm == NULL) + if (wpa_auth == NULL) return; + group = wpa_auth->group; + for (i = 0; i < 2; i++) { - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; + wpa_gtk_update(wpa_auth, group); } } @@ -2748,151 +3480,317 @@ static int wpa_cipher_bits(int cipher) #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { - int len = 0, i; - char pmkid_txt[PMKID_LEN * 2 + 1], *pos; - - len += snprintf(buf + len, buflen - len, - "dot11RSNAOptionImplemented=TRUE\n" - "dot11RSNAPreauthenticationImplemented=TRUE\n" - "dot11RSNAEnabled=%s\n" - "dot11RSNAPreauthenticationEnabled=%s\n", - wpa_bool_txt(hapd->conf->wpa & - HOSTAPD_WPA_VERSION_WPA2), - wpa_bool_txt(hapd->conf->rsn_preauth)); + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; - if (hapd->wpa_auth == NULL) + if (wpa_auth == NULL) return len; - pos = pmkid_txt; - for (i = 0; i < PMKID_LEN; i++) { - pos += sprintf(pos, "%02x", - hapd->wpa_auth->dot11RSNAPMKIDUsed[i]); - } - - len += snprintf(buf + len, buflen - len, - "dot11RSNAConfigVersion=%u\n" - "dot11RSNAConfigPairwiseKeysSupported=9999\n" - /* FIX: dot11RSNAConfigGroupCipher */ - /* FIX: dot11RSNAConfigGroupRekeyMethod */ - /* FIX: dot11RSNAConfigGroupRekeyTime */ - /* FIX: dot11RSNAConfigGroupRekeyPackets */ - "dot11RSNAConfigGroupRekeyStrict=%u\n" - "dot11RSNAConfigGroupUpdateCount=%u\n" - "dot11RSNAConfigPairwiseUpdateCount=%u\n" - "dot11RSNAConfigGroupCipherSize=%u\n" - "dot11RSNAConfigPMKLifetime=%u\n" - "dot11RSNAConfigPMKReauthThreshold=%u\n" - "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" - "dot11RSNAConfigSATimeout=%u\n" - "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" - "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" - "dot11RSNAPMKIDUsed=%s\n" - "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" - "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" - "dot11RSNATKIPCounterMeasuresInvoked=%u\n" - "dot11RSNA4WayHandshakeFailures=%u\n" - "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", - RSN_VERSION, - !!hapd->conf->wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(hapd->conf->wpa_group), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherSelected), - pmkid_txt, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherRequested), - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, - hapd->wpa_auth->dot11RSNA4WayHandshakeFailures); + ret = snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" +#ifdef CONFIG_RSN_PREAUTH + "dot11RSNAPreauthenticationImplemented=TRUE\n" +#else /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=FALSE\n" +#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(wpa_auth->conf.wpa & + HOSTAPD_WPA_VERSION_WPA2), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = snprintf(buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(wpa_auth->conf.wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* TODO: dot11RSNAConfigPairwiseCiphersTable */ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAGroupState=%d\n", - hapd->wpa_auth->wpa_group_state); + ret = snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } -int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { - int len = 0; + int len = 0, ret; u8 not_used[4] = { 0, 0, 0, 0 }; const u8 *pairwise = not_used; + if (sm == NULL) + return 0; + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ /* dot11RSNAStatsEntry */ - if (sta->wpa == WPA_VERSION_WPA) { - if (sta->pairwise == WPA_CIPHER_CCMP) + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sta->wpa == WPA_VERSION_WPA2) { - if (sta->pairwise == WPA_CIPHER_CCMP) + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = RSN_CIPHER_SUITE_NONE; } else return 0; - len += snprintf(buf + len, buflen - len, - /* TODO: dot11RSNAStatsIndex */ - "dot11RSNAStatsSTAAddress=" MACSTR "\n" - "dot11RSNAStatsVersion=1\n" - "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" - /* TODO: dot11RSNAStatsTKIPICVErrors */ - "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" - /* TODO: dot11RSNAStatsCCMPReplays */ - /* TODO: dot11RSNAStatsCCMPDecryptErrors */ - /* TODO: dot11RSNAStatsTKIPReplays */, - MAC2STR(sta->addr), - RSN_SUITE_ARG(pairwise), - sta->dot11RSNAStatsTKIPLocalMICFailures, - sta->dot11RSNAStatsTKIPRemoteMICFailures); - - if (sta->wpa_sm == NULL) + ret = snprintf(buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) return len; + len += ret; /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAPTKState=%d\n" - "hostapdWPAPTKGroupState=%d\n", - sta->wpa_sm->wpa_ptk_state, - sta->wpa_sm->wpa_ptk_group_state); + ret = snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + return -1; + + if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, WPA_PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol)) + return 0; + + return -1; +} + + +static struct wpa_group * +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + if (sm->group && sm->group != group && sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + wpa_printf(MSG_DEBUG, "WLA: Decreased GNoStations for the " + "previously used group state machine"); + } + + sm->group = group; + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ |