From a9d1364c5fd29a4afe2b45443c6e539ee6cc1e4e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 7 Dec 2008 19:24:56 +0200 Subject: [PATCH] Merged EAP-AKA' into eap_aka.c and added it to defconfig/ChangeLog --- hostapd/ChangeLog | 1 + hostapd/Makefile | 2 - hostapd/defconfig | 4 + src/eap_common/eap_sim_common.h | 27 + src/eap_peer/eap_aka.c | 302 ++++++- src/eap_peer/eap_aka_prime.c | 1373 ------------------------------- src/eap_server/eap_aka.c | 309 ++++++- src/eap_server/eap_aka_prime.c | 1265 ---------------------------- wpa_supplicant/ChangeLog | 2 + wpa_supplicant/Makefile | 5 - wpa_supplicant/defconfig | 4 + wpa_supplicant/eap_testing.txt | 1 + www/hostapd/index.html | 3 +- www/wpa_supplicant/index.html | 3 +- 14 files changed, 579 insertions(+), 2722 deletions(-) delete mode 100644 src/eap_peer/eap_aka_prime.c delete mode 100644 src/eap_server/eap_aka_prime.c diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index 04b310082..586eb5e97 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -15,6 +15,7 @@ ChangeLog for hostapd information (added if ieee80211d=1 in configuration) * fixed WEP authentication (both Open System and Shared Key) with mac80211 + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) 2008-11-23 - v0.6.6 * added a new configuration option, wpa_ptk_rekey, that can be used to diff --git a/hostapd/Makefile b/hostapd/Makefile index 19324d54e..5061413b9 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -214,8 +214,6 @@ endif ifdef CONFIG_EAP_AKA_PRIME CFLAGS += -DEAP_AKA_PRIME -OBJS += ../src/eap_server/eap_aka_prime.o -CONFIG_EAP_SIM_COMMON=y endif ifdef CONFIG_EAP_SIM_COMMON diff --git a/hostapd/defconfig b/hostapd/defconfig index cbbd19358..3daa6f93f 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -80,6 +80,10 @@ CONFIG_EAP_TTLS=y # EAP-AKA for the integrated EAP server #CONFIG_EAP_AKA=y +# EAP-AKA' for the integrated EAP server +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + # EAP-PAX for the integrated EAP server #CONFIG_EAP_PAX=y diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h index 22f56ed2f..a8080e27a 100644 --- a/src/eap_common/eap_sim_common.h +++ b/src/eap_common/eap_sim_common.h @@ -94,6 +94,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); +#ifdef EAP_AKA_PRIME void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *k_encr, u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk); @@ -109,6 +110,32 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, const u8 *network_name, size_t network_name_len); +#else /* EAP_AKA_PRIME */ +static inline void eap_aka_prime_derive_keys(const u8 *identity, + size_t identity_len, + const u8 *ik, const u8 *ck, + u8 *k_encr, u8 *k_aut, u8 *k_re, + u8 *msk, u8 *emsk) +{ +} + +static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, + size_t identity_len, + const u8 *nonce_s, u8 *msk, + u8 *emsk) +{ + return -1; +} + +static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + return -1; +} +#endif /* EAP_AKA_PRIME */ /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index d8f07f638..ff68dfbbf 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -1,6 +1,6 @@ /* - * EAP peer method: EAP-AKA (RFC 4187) - * Copyright (c) 2004-2007, Jouni Malinen + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) + * Copyright (c) 2004-2008, Jouni Malinen * * 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 @@ -19,6 +19,7 @@ #include "pcsc_funcs.h" #include "eap_common/eap_sim_common.h" #include "sha1.h" +#include "sha256.h" #include "crypto.h" #include "eap_peer/eap_config.h" #ifdef CONFIG_USIM_SIMULATOR @@ -31,8 +32,9 @@ struct eap_aka_data { size_t res_len; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; u8 mk[EAP_SIM_MK_LEN]; - u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; @@ -54,6 +56,10 @@ struct eap_aka_data { struct wpabuf *id_msgs; int prev_id; int result_ind, use_result_ind; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; }; @@ -96,6 +102,8 @@ static void * eap_aka_init(struct eap_sm *sm) if (data == NULL) return NULL; + data->eap_method = EAP_TYPE_AKA; + eap_aka_state(data, CONTINUE); data->prev_id = -1; @@ -105,6 +113,18 @@ static void * eap_aka_init(struct eap_sm *sm) } +#ifdef EAP_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data = eap_aka_init(sm); + if (data == NULL) + return NULL; + data->eap_method = EAP_TYPE_AKA_PRIME; + return data; +} +#endif /* EAP_AKA_PRIME */ + + static void eap_aka_deinit(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; @@ -113,6 +133,7 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv) os_free(data->reauth_id); os_free(data->last_eap_identity); wpabuf_free(data->id_msgs); + os_free(data->network_name); os_free(data); } } @@ -302,7 +323,7 @@ static void eap_aka_add_checkcode(struct eap_aka_data *data, { const u8 *addr; size_t len; - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); @@ -315,14 +336,18 @@ static void eap_aka_add_checkcode(struct eap_aka_data *data, return; } - /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); - sha1_vector(1, &addr, &len, hash); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, - EAP_AKA_CHECKCODE_LEN); + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); } @@ -331,7 +356,8 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, { const u8 *addr; size_t len; - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; if (checkcode == NULL) return -1; @@ -346,19 +372,25 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, return 0; } - if (checkcode_len != EAP_AKA_CHECKCODE_LEN) { + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " "indicates that AKA/Identity message were not " "used, but they were"); return -1; } - /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); - sha1_vector(1, &addr, &len, hash); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); - if (os_memcmp(hash, checkcode, EAP_AKA_CHECKCODE_LEN) != 0) { + if (os_memcmp(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } @@ -376,7 +408,7 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, data->num_id_req = 0; data->num_notification = 0; - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); return eap_sim_msg_finish(msg, NULL, NULL, 0); @@ -394,7 +426,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " "(id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); return eap_sim_msg_finish(msg, NULL, NULL, 0); } @@ -410,7 +442,7 @@ static struct wpabuf * eap_aka_synchronization_failure( wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " "(id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, @@ -449,7 +481,7 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, eap_aka_clear_identities(data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); if (identity) { @@ -469,7 +501,7 @@ static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RES"); eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, @@ -494,7 +526,7 @@ static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_REAUTHENTICATION); wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); @@ -535,7 +567,7 @@ static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_NOTIFICATION); if (k_aut && data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); @@ -611,6 +643,101 @@ static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, } +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); +} + + +#ifdef EAP_AKA_PRIME +static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, + u8 id, u16 kdf) +{ + struct eap_sim_msg *msg; + + data->kdf = kdf; + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " + "select)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, + u8 id, struct eap_sim_attrs *attr) +{ + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_select(data, id, + EAP_AKA_PRIME_KDF); + } + + /* No matching KDF found - fail authentication as if AUTN had been + * incorrect */ + return eap_aka_authentication_reject(data, id); +} + + +static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + size_t i, j; + + if (attr->kdf_count == 0) + return 0; + + /* The only allowed (and required) duplication of a KDF is the addition + * of the selected KDF into the beginning of the list. */ + + if (data->kdf) { + if (attr->kdf[0] != data->kdf) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "accept the selected KDF"); + return 0; + } + + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] == data->kdf) + break; + } + if (i == attr->kdf_count && + attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "duplicate the selected KDF"); + return 0; + } + + /* TODO: should check that the list is identical to the one + * used in the previous Challenge message apart from the added + * entry in the beginning. */ + } + + for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { + for (j = i + 1; j < attr->kdf_count; j++) { + if (attr->kdf[i] == attr->kdf[j]) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server " + "included a duplicated KDF"); + return 0; + } + } + } + + return 1; +} +#endif /* EAP_AKA_PRIME */ + + static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, u8 id, @@ -633,6 +760,52 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (!attr->kdf_input || attr->kdf_input_len == 0) { + wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " + "did not include non-empty AT_KDF_INPUT"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + os_free(data->network_name); + data->network_name = os_malloc(attr->kdf_input_len); + if (data->network_name == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " + "storing Network Name"); + return eap_aka_authentication_reject(data, id); + } + os_memcpy(data->network_name, attr->kdf_input, + attr->kdf_input_len); + data->network_name_len = attr->kdf_input_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " + "(AT_KDF_INPUT)", + data->network_name, data->network_name_len); + /* TODO: check Network Name per 3GPP.33.402 */ + + if (!eap_aka_prime_kdf_valid(data, attr)) + return eap_aka_authentication_reject(data, id); + + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_neg(data, id, attr); + + data->kdf = EAP_AKA_PRIME_KDF; + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + } + + if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { + u16 flags = WPA_GET_BE16(attr->bidding); + if ((flags & EAP_AKA_BIDDING_FLAG_D) && + eap_allowed_method(sm, EAP_VENDOR_IETF, + EAP_TYPE_AKA_PRIME)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " + "AKA' to AKA detected"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + } +#endif /* EAP_AKA_PRIME */ + data->reauth = 0; if (!attr->mac || !attr->rand || !attr->autn) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " @@ -660,6 +833,16 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^AK for CK',IK' derivation */ + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_AKA_PRIME */ if (data->last_eap_identity) { identity = data->last_eap_identity; identity_len = data->last_eap_identity_len; @@ -670,12 +853,17 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, identity = eap_get_config_identity(sm, &identity_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); - eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, - data->mk); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, - data->emsk); - if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) - { + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, + data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "used invalid AT_MAC"); return eap_aka_client_error(data, id, @@ -763,8 +951,7 @@ static int eap_aka_process_notification_auth(struct eap_aka_data *data, return -1; } - if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) - { + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " "used invalid AT_MAC"); return -1; @@ -842,8 +1029,7 @@ static struct wpabuf * eap_aka_process_reauthentication( } data->reauth = 1; - if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) - { + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "did not have valid AT_MAC"); return eap_aka_client_error(data, id, @@ -882,10 +1068,6 @@ static struct wpabuf * eap_aka_process_reauthentication( "(%d <= %d)", eattr.counter, data->counter); data->counter_too_small = eattr.counter; - eap_sim_derive_keys_reauth(eattr.counter, data->reauth_id, - data->reauth_id_len, eattr.nonce_s, - data->mk, NULL, NULL); - /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. * However, since it was used in the last EAP-Response-Identity @@ -908,10 +1090,18 @@ static struct wpabuf * eap_aka_process_reauthentication( wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); - eap_sim_derive_keys_reauth(data->counter, - data->reauth_id, data->reauth_id_len, - data->nonce_s, data->mk, data->msk, - data->emsk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + data->reauth_id, + data->reauth_id_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys_reauth(data->counter, data->reauth_id, + data->reauth_id_len, + data->nonce_s, data->mk, + data->msk, data->emsk); + } eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_aka_learn_ids(data, &eattr); @@ -955,7 +1145,8 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, return NULL; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_AKA, reqData, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, + &len); if (pos == NULL || len < 1) { ret->ignore = TRUE; return NULL; @@ -973,7 +1164,8 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); pos += 2; /* Reserved */ - if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 1, + if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 0)) { res = eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); @@ -1152,3 +1344,35 @@ int eap_peer_aka_register(void) eap_peer_method_free(eap); return ret; } + + +#ifdef EAP_AKA_PRIME +int eap_peer_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->deinit = eap_aka_deinit; + eap->process = eap_aka_process; + eap->isKeyAvailable = eap_aka_isKeyAvailable; + eap->getKey = eap_aka_getKey; + eap->has_reauth_data = eap_aka_has_reauth_data; + eap->deinit_for_reauth = eap_aka_deinit_for_reauth; + eap->init_for_reauth = eap_aka_init_for_reauth; + eap->get_identity = eap_aka_get_identity; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + + return ret; +} +#endif /* EAP_AKA_PRIME */ diff --git a/src/eap_peer/eap_aka_prime.c b/src/eap_peer/eap_aka_prime.c deleted file mode 100644 index bf1d472e6..000000000 --- a/src/eap_peer/eap_aka_prime.c +++ /dev/null @@ -1,1373 +0,0 @@ -/* - * EAP peer method: EAP-AKA' (draft-arkko-eap-aka-kdf-10.txt) - * Copyright (c) 2004-2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eap_peer/eap_i.h" -#include "pcsc_funcs.h" -#include "eap_common/eap_sim_common.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" -#include "eap_peer/eap_config.h" -#ifdef CONFIG_USIM_SIMULATOR -#include "hlr_auc_gw/milenage.h" -#endif /* CONFIG_USIM_SIMULATOR */ - - -struct eap_aka_data { - u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; - size_t res_len; - u8 nonce_s[EAP_SIM_NONCE_S_LEN]; - u8 mk[EAP_SIM_MK_LEN]; - u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; - u8 k_encr[EAP_SIM_K_ENCR_LEN]; - u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; - u8 msk[EAP_SIM_KEYING_DATA_LEN]; - u8 emsk[EAP_EMSK_LEN]; - u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; - u8 auts[EAP_AKA_AUTS_LEN]; - - int num_id_req, num_notification; - u8 *pseudonym; - size_t pseudonym_len; - u8 *reauth_id; - size_t reauth_id_len; - int reauth; - unsigned int counter, counter_too_small; - u8 *last_eap_identity; - size_t last_eap_identity_len; - enum { - CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE - } state; - - struct wpabuf *id_msgs; - int prev_id; - int result_ind, use_result_ind; - u8 eap_method; - u8 *network_name; - size_t network_name_len; - u16 kdf; -}; - - -#ifndef CONFIG_NO_STDOUT_DEBUG -static const char * eap_aka_state_txt(int state) -{ - switch (state) { - case CONTINUE: - return "CONTINUE"; - case RESULT_SUCCESS: - return "RESULT_SUCCESS"; - case RESULT_FAILURE: - return "RESULT_FAILURE"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - default: - return "?"; - } -} -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -static void eap_aka_state(struct eap_aka_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA%s: %s -> %s", - data->eap_method == EAP_TYPE_AKA_PRIME ? "'" : "", - eap_aka_state_txt(data->state), - eap_aka_state_txt(state)); - data->state = state; -} - - -static void * eap_aka_init(struct eap_sm *sm) -{ - struct eap_aka_data *data; - const char *phase1 = eap_get_config_phase1(sm); - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - - data->eap_method = EAP_TYPE_AKA; - - eap_aka_state(data, CONTINUE); - data->prev_id = -1; - - data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; - - return data; -} - - -static void * eap_aka_prime_init(struct eap_sm *sm) -{ - struct eap_aka_data *data = eap_aka_init(sm); - if (data == NULL) - return NULL; - data->eap_method = EAP_TYPE_AKA_PRIME; - return data; -} - - -static void eap_aka_deinit(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - if (data) { - os_free(data->pseudonym); - os_free(data->reauth_id); - os_free(data->last_eap_identity); - wpabuf_free(data->id_msgs); - os_free(data->network_name); - os_free(data); - } -} - - -static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) -{ - struct eap_peer_config *conf; - - wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); - - conf = eap_get_config(sm); - if (conf == NULL) - return -1; - if (conf->pcsc) { - return scard_umts_auth(sm->scard_ctx, data->rand, - data->autn, data->res, &data->res_len, - data->ik, data->ck, data->auts); - } - -#ifdef CONFIG_USIM_SIMULATOR - if (conf->password) { - u8 opc[16], k[16], sqn[6]; - const char *pos; - wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " - "implementation for UMTS authentication"); - if (conf->password_len < 78) { - wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " - "password"); - return -1; - } - pos = (const char *) conf->password; - if (hexstr2bin(pos, k, 16)) - return -1; - pos += 32; - if (*pos != ':') - return -1; - pos++; - - if (hexstr2bin(pos, opc, 16)) - return -1; - pos += 32; - if (*pos != ':') - return -1; - pos++; - - if (hexstr2bin(pos, sqn, 6)) - return -1; - - return milenage_check(opc, k, sqn, data->rand, data->autn, - data->ik, data->ck, - data->res, &data->res_len, data->auts); - } -#endif /* CONFIG_USIM_SIMULATOR */ - -#ifdef CONFIG_USIM_HARDCODED - wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " - "testing"); - - /* These hardcoded Kc and SRES values are used for testing. - * Could consider making them configurable. */ - os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); - data->res_len = EAP_AKA_RES_MAX_LEN; - os_memset(data->ik, '3', EAP_AKA_IK_LEN); - os_memset(data->ck, '4', EAP_AKA_CK_LEN); - { - u8 autn[EAP_AKA_AUTN_LEN]; - os_memset(autn, '1', EAP_AKA_AUTN_LEN); - if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { - wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " - "with expected value"); - return -1; - } - } -#if 0 - { - static int test_resync = 1; - if (test_resync) { - /* Test Resynchronization */ - test_resync = 0; - return -2; - } - } -#endif - return 0; - -#else /* CONFIG_USIM_HARDCODED */ - - wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " - "enabled"); - return -1; - -#endif /* CONFIG_USIM_HARDCODED */ -} - - -#define CLEAR_PSEUDONYM 0x01 -#define CLEAR_REAUTH_ID 0x02 -#define CLEAR_EAP_ID 0x04 - -static void eap_aka_clear_identities(struct eap_aka_data *data, int id) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", - id & CLEAR_PSEUDONYM ? " pseudonym" : "", - id & CLEAR_REAUTH_ID ? " reauth_id" : "", - id & CLEAR_EAP_ID ? " eap_id" : ""); - if (id & CLEAR_PSEUDONYM) { - os_free(data->pseudonym); - data->pseudonym = NULL; - data->pseudonym_len = 0; - } - if (id & CLEAR_REAUTH_ID) { - os_free(data->reauth_id); - data->reauth_id = NULL; - data->reauth_id_len = 0; - } - if (id & CLEAR_EAP_ID) { - os_free(data->last_eap_identity); - data->last_eap_identity = NULL; - data->last_eap_identity_len = 0; - } -} - - -static int eap_aka_learn_ids(struct eap_aka_data *data, - struct eap_sim_attrs *attr) -{ - if (attr->next_pseudonym) { - os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); - if (data->pseudonym == NULL) { - wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " - "next pseudonym"); - return -1; - } - os_memcpy(data->pseudonym, attr->next_pseudonym, - attr->next_pseudonym_len); - data->pseudonym_len = attr->next_pseudonym_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", - data->pseudonym, - data->pseudonym_len); - } - - if (attr->next_reauth_id) { - os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); - if (data->reauth_id == NULL) { - wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " - "next reauth_id"); - return -1; - } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); - data->reauth_id_len = attr->next_reauth_id_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", - data->reauth_id, - data->reauth_id_len); - } - - return 0; -} - - -static int eap_aka_add_id_msg(struct eap_aka_data *data, - const struct wpabuf *msg) -{ - if (msg == NULL) - return -1; - - if (data->id_msgs == NULL) { - data->id_msgs = wpabuf_dup(msg); - return data->id_msgs == NULL ? -1 : 0; - } - - if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) - return -1; - wpabuf_put_buf(data->id_msgs, msg); - - return 0; -} - - -static void eap_aka_add_checkcode(struct eap_aka_data *data, - struct eap_sim_msg *msg) -{ - const u8 *addr; - size_t len; - u8 hash[SHA256_MAC_LEN]; - - wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); - - if (data->id_msgs == NULL) { - /* - * No EAP-AKA/Identity packets were exchanged - send empty - * checkcode. - */ - eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); - return; - } - - /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ - addr = wpabuf_head(data->id_msgs); - len = wpabuf_len(data->id_msgs); - wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); - if (data->eap_method == EAP_TYPE_AKA_PRIME) - sha256_vector(1, &addr, &len, hash); - else - sha1_vector(1, &addr, &len, hash); - - eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, - data->eap_method == EAP_TYPE_AKA_PRIME ? - EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); -} - - -static int eap_aka_verify_checkcode(struct eap_aka_data *data, - const u8 *checkcode, size_t checkcode_len) -{ - const u8 *addr; - size_t len; - u8 hash[SHA256_MAC_LEN]; - size_t hash_len; - - if (checkcode == NULL) - return -1; - - if (data->id_msgs == NULL) { - if (checkcode_len != 0) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " - "indicates that AKA/Identity messages were " - "used, but they were not"); - return -1; - } - return 0; - } - - hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? - EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; - - if (checkcode_len != hash_len) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " - "indicates that AKA/Identity message were not " - "used, but they were"); - return -1; - } - - /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ - addr = wpabuf_head(data->id_msgs); - len = wpabuf_len(data->id_msgs); - if (data->eap_method == EAP_TYPE_AKA_PRIME) - sha256_vector(1, &addr, &len, hash); - else - sha1_vector(1, &addr, &len, hash); - - if (os_memcmp(hash, checkcode, hash_len) != 0) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); - return -1; - } - - return 0; -} - - -static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, - int err) -{ - struct eap_sim_msg *msg; - - eap_aka_state(data, FAILURE); - data->num_id_req = 0; - data->num_notification = 0; - - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_CLIENT_ERROR); - eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); - return eap_sim_msg_finish(msg, NULL, NULL, 0); -} - - -static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, - u8 id) -{ - struct eap_sim_msg *msg; - - eap_aka_state(data, FAILURE); - data->num_id_req = 0; - data->num_notification = 0; - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " - "(id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); - return eap_sim_msg_finish(msg, NULL, NULL, 0); -} - - -static struct wpabuf * eap_aka_synchronization_failure( - struct eap_aka_data *data, u8 id) -{ - struct eap_sim_msg *msg; - - data->num_id_req = 0; - data->num_notification = 0; - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " - "(id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); - wpa_printf(MSG_DEBUG, " AT_AUTS"); - eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, - EAP_AKA_AUTS_LEN); - return eap_sim_msg_finish(msg, NULL, NULL, 0); -} - - -static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, - struct eap_aka_data *data, - u8 id, - enum eap_sim_id_req id_req) -{ - const u8 *identity = NULL; - size_t identity_len = 0; - struct eap_sim_msg *msg; - - data->reauth = 0; - if (id_req == ANY_ID && data->reauth_id) { - identity = data->reauth_id; - identity_len = data->reauth_id_len; - data->reauth = 1; - } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && - data->pseudonym) { - identity = data->pseudonym; - identity_len = data->pseudonym_len; - eap_aka_clear_identities(data, CLEAR_REAUTH_ID); - } else if (id_req != NO_ID_REQ) { - identity = eap_get_config_identity(sm, &identity_len); - if (identity) { - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | - CLEAR_REAUTH_ID); - } - } - if (id_req != NO_ID_REQ) - eap_aka_clear_identities(data, CLEAR_EAP_ID); - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_IDENTITY); - - if (identity) { - wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", - identity, identity_len); - eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, - identity, identity_len); - } - - return eap_sim_msg_finish(msg, NULL, NULL, 0); -} - - -static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, - u8 id) -{ - struct eap_sim_msg *msg; - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_CHALLENGE); - wpa_printf(MSG_DEBUG, " AT_RES"); - eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, - data->res, data->res_len); - eap_aka_add_checkcode(data, msg); - if (data->use_result_ind) { - wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); - eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); - } - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); -} - - -static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, - u8 id, int counter_too_small, - const u8 *nonce_s) -{ - struct eap_sim_msg *msg; - unsigned int counter; - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", - id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_REAUTHENTICATION); - wpa_printf(MSG_DEBUG, " AT_IV"); - wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); - eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); - - if (counter_too_small) { - wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); - eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); - counter = data->counter_too_small; - } else - counter = data->counter; - - wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); - eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); - - if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " - "AT_ENCR_DATA"); - eap_sim_msg_free(msg); - return NULL; - } - eap_aka_add_checkcode(data, msg); - if (data->use_result_ind) { - wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); - eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); - } - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, nonce_s, - EAP_SIM_NONCE_S_LEN); -} - - -static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, - u8 id, u16 notification) -{ - struct eap_sim_msg *msg; - u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; - - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_NOTIFICATION); - if (k_aut && data->reauth) { - wpa_printf(MSG_DEBUG, " AT_IV"); - wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); - eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, - EAP_SIM_AT_ENCR_DATA); - wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); - eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, - NULL, 0); - if (eap_sim_msg_add_encr_end(msg, data->k_encr, - EAP_SIM_AT_PADDING)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " - "AT_ENCR_DATA"); - eap_sim_msg_free(msg); - return NULL; - } - } - if (k_aut) { - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - } - return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); -} - - -static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, - struct eap_aka_data *data, - u8 id, - const struct wpabuf *reqData, - struct eap_sim_attrs *attr) -{ - int id_error; - struct wpabuf *buf; - - wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); - - id_error = 0; - switch (attr->id_req) { - case NO_ID_REQ: - break; - case ANY_ID: - if (data->num_id_req > 0) - id_error++; - data->num_id_req++; - break; - case FULLAUTH_ID: - if (data->num_id_req > 1) - id_error++; - data->num_id_req++; - break; - case PERMANENT_ID: - if (data->num_id_req > 2) - id_error++; - data->num_id_req++; - break; - } - if (id_error) { - wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " - "used within one authentication"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - buf = eap_aka_response_identity(sm, data, id, attr->id_req); - - if (data->prev_id != id) { - eap_aka_add_id_msg(data, reqData); - eap_aka_add_id_msg(data, buf); - data->prev_id = id; - } - - return buf; -} - - -static int eap_aka_verify_mac(struct eap_aka_data *data, - const struct wpabuf *req, - const u8 *mac, const u8 *extra, - size_t extra_len) -{ - if (data->eap_method == EAP_TYPE_AKA_PRIME) - return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, - extra_len); - return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); -} - - -#ifdef EAP_AKA_PRIME -static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, - u8 id, u16 kdf) -{ - struct eap_sim_msg *msg; - - data->kdf = kdf; - wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " - "select)", id); - msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, - EAP_AKA_SUBTYPE_CHALLENGE); - wpa_printf(MSG_DEBUG, " AT_KDF"); - eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); - return eap_sim_msg_finish(msg, NULL, NULL, 0); -} - - -static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, - u8 id, struct eap_sim_attrs *attr) -{ - size_t i; - - for (i = 0; i < attr->kdf_count; i++) { - if (attr->kdf[i] == EAP_AKA_PRIME_KDF) - return eap_aka_prime_kdf_select(data, id, - EAP_AKA_PRIME_KDF); - } - - /* No matching KDF found - fail authentication as if AUTN had been - * incorrect */ - return eap_aka_authentication_reject(data, id); -} - - -static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, - struct eap_sim_attrs *attr) -{ - size_t i, j; - - if (attr->kdf_count == 0) - return 0; - - /* The only allowed (and required) duplication of a KDF is the addition - * of the selected KDF into the beginning of the list. */ - - if (data->kdf) { - if (attr->kdf[0] != data->kdf) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " - "accept the selected KDF"); - return 0; - } - - for (i = 1; i < attr->kdf_count; i++) { - if (attr->kdf[i] == data->kdf) - break; - } - if (i == attr->kdf_count && - attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " - "duplicate the selected KDF"); - return 0; - } - - /* TODO: should check that the list is identical to the one - * used in the previous Challenge message apart from the added - * entry in the beginning. */ - } - - for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { - for (j = i + 1; j < attr->kdf_count; j++) { - if (attr->kdf[i] == attr->kdf[j]) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server " - "included a duplicated KDF"); - return 0; - } - } - } - - return 1; -} -#endif /* EAP_AKA_PRIME */ - - -static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, - struct eap_aka_data *data, - u8 id, - const struct wpabuf *reqData, - struct eap_sim_attrs *attr) -{ - const u8 *identity; - size_t identity_len; - int res; - struct eap_sim_attrs eattr; - - wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); - - if (attr->checkcode && - eap_aka_verify_checkcode(data, attr->checkcode, - attr->checkcode_len)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " - "message"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - -#ifdef EAP_AKA_PRIME - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - if (!attr->kdf_input || attr->kdf_input_len == 0) { - wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " - "did not include non-empty AT_KDF_INPUT"); - /* Fail authentication as if AUTN had been incorrect */ - return eap_aka_authentication_reject(data, id); - } - os_free(data->network_name); - data->network_name = os_malloc(attr->kdf_input_len); - if (data->network_name == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " - "storing Network Name"); - return eap_aka_authentication_reject(data, id); - } - os_memcpy(data->network_name, attr->kdf_input, - attr->kdf_input_len); - data->network_name_len = attr->kdf_input_len; - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " - "(AT_KDF_INPUT)", - data->network_name, data->network_name_len); - /* TODO: check Network Name per 3GPP.33.402 */ - - if (!eap_aka_prime_kdf_valid(data, attr)) - return eap_aka_authentication_reject(data, id); - - if (attr->kdf[0] != EAP_AKA_PRIME_KDF) - return eap_aka_prime_kdf_neg(data, id, attr); - - data->kdf = EAP_AKA_PRIME_KDF; - wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); - } - - if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { - u16 flags = WPA_GET_BE16(attr->bidding); - if ((flags & EAP_AKA_BIDDING_FLAG_D) && - eap_allowed_method(sm, EAP_VENDOR_IETF, - EAP_TYPE_AKA_PRIME)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " - "AKA' to AKA detected"); - /* Fail authentication as if AUTN had been incorrect */ - return eap_aka_authentication_reject(data, id); - } - } -#endif /* EAP_AKA_PRIME */ - - data->reauth = 0; - if (!attr->mac || !attr->rand || !attr->autn) { - wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " - "did not include%s%s%s", - !attr->mac ? " AT_MAC" : "", - !attr->rand ? " AT_RAND" : "", - !attr->autn ? " AT_AUTN" : ""); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); - os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); - - res = eap_aka_umts_auth(sm, data); - if (res == -1) { - wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " - "failed (AUTN)"); - return eap_aka_authentication_reject(data, id); - } else if (res == -2) { - wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " - "failed (AUTN seq# -> AUTS)"); - return eap_aka_synchronization_failure(data, id); - } else if (res) { - wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } -#ifdef EAP_AKA_PRIME - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the - * needed 6-octet SQN ^AK for CK',IK' derivation */ - eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, - data->autn, - data->network_name, - data->network_name_len); - } -#endif /* EAP_AKA_PRIME */ - if (data->last_eap_identity) { - identity = data->last_eap_identity; - identity_len = data->last_eap_identity_len; - } else if (data->pseudonym) { - identity = data->pseudonym; - identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_len); - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " - "derivation", identity, identity_len); - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys(identity, identity_len, data->ik, - data->ck, data->k_encr, data->k_aut, - data->k_re, data->msk, data->emsk); - } else { - eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, - data->mk); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, - data->msk, data->emsk); - } - if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " - "used invalid AT_MAC"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - /* Old reauthentication and pseudonym identities must not be used - * anymore. In other words, if no new identities are received, full - * authentication will be used on next reauthentication. */ - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | - CLEAR_EAP_ID); - - if (attr->encr_data) { - u8 *decrypted; - decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, - &eattr, 0); - if (decrypted == NULL) { - return eap_aka_client_error( - data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - eap_aka_learn_ids(data, &eattr); - os_free(decrypted); - } - - if (data->result_ind && attr->result_ind) - data->use_result_ind = 1; - - if (data->state != FAILURE && data->state != RESULT_FAILURE) { - eap_aka_state(data, data->use_result_ind ? - RESULT_SUCCESS : SUCCESS); - } - - data->num_id_req = 0; - data->num_notification = 0; - /* RFC 4187 specifies that counter is initialized to one after - * fullauth, but initializing it to zero makes it easier to implement - * reauth verification. */ - data->counter = 0; - return eap_aka_response_challenge(data, id); -} - - -static int eap_aka_process_notification_reauth(struct eap_aka_data *data, - struct eap_sim_attrs *attr) -{ - struct eap_sim_attrs eattr; - u8 *decrypted; - - if (attr->encr_data == NULL || attr->iv == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " - "reauth did not include encrypted data"); - return -1; - } - - decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, - 0); - if (decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " - "data from notification message"); - return -1; - } - - if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { - wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " - "message does not match with counter in reauth " - "message"); - os_free(decrypted); - return -1; - } - - os_free(decrypted); - return 0; -} - - -static int eap_aka_process_notification_auth(struct eap_aka_data *data, - const struct wpabuf *reqData, - struct eap_sim_attrs *attr) -{ - if (attr->mac == NULL) { - wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " - "Notification message"); - return -1; - } - - if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " - "used invalid AT_MAC"); - return -1; - } - - if (data->reauth && - eap_aka_process_notification_reauth(data, attr)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " - "message after reauth"); - return -1; - } - - return 0; -} - - -static struct wpabuf * eap_aka_process_notification( - struct eap_sm *sm, struct eap_aka_data *data, u8 id, - const struct wpabuf *reqData, struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); - if (data->num_notification > 0) { - wpa_printf(MSG_INFO, "EAP-AKA: too many notification " - "rounds (only one allowed)"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - data->num_notification++; - if (attr->notification == -1) { - wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " - "Notification message"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - if ((attr->notification & 0x4000) == 0 && - eap_aka_process_notification_auth(data, reqData, attr)) { - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); - if (attr->notification >= 0 && attr->notification < 32768) { - eap_aka_state(data, FAILURE); - } else if (attr->notification == EAP_SIM_SUCCESS && - data->state == RESULT_SUCCESS) - eap_aka_state(data, SUCCESS); - return eap_aka_response_notification(data, id, attr->notification); -} - - -static struct wpabuf * eap_aka_process_reauthentication( - struct eap_sm *sm, struct eap_aka_data *data, u8 id, - const struct wpabuf *reqData, struct eap_sim_attrs *attr) -{ - struct eap_sim_attrs eattr; - u8 *decrypted; - - wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); - - if (attr->checkcode && - eap_aka_verify_checkcode(data, attr->checkcode, - attr->checkcode_len)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " - "message"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - if (data->reauth_id == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " - "reauthentication, but no reauth_id available"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - data->reauth = 1; - if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " - "did not have valid AT_MAC"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - if (attr->encr_data == NULL || attr->iv == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " - "message did not include encrypted data"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, - 0); - if (decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " - "data from reauthentication message"); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - if (eattr.nonce_s == NULL || eattr.counter < 0) { - wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", - !eattr.nonce_s ? " AT_NONCE_S" : "", - eattr.counter < 0 ? " AT_COUNTER" : ""); - os_free(decrypted); - return eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - } - - if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { - struct wpabuf *res; - wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " - "(%d <= %d)", eattr.counter, data->counter); - data->counter_too_small = eattr.counter; - - /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current - * reauth_id must not be used to start a new reauthentication. - * However, since it was used in the last EAP-Response-Identity - * packet, it has to saved for the following fullauth to be - * used in MK derivation. */ - os_free(data->last_eap_identity); - data->last_eap_identity = data->reauth_id; - data->last_eap_identity_len = data->reauth_id_len; - data->reauth_id = NULL; - data->reauth_id_len = 0; - - res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); - os_free(decrypted); - - return res; - } - data->counter = eattr.counter; - - os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", - data->nonce_s, EAP_SIM_NONCE_S_LEN); - - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, - data->reauth_id, - data->reauth_id_len, - data->nonce_s, - data->msk, data->emsk); - } else { - eap_sim_derive_keys_reauth(data->counter, data->reauth_id, - data->reauth_id_len, - data->nonce_s, data->mk, - data->msk, data->emsk); - } - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - eap_aka_learn_ids(data, &eattr); - - if (data->result_ind && attr->result_ind) - data->use_result_ind = 1; - - if (data->state != FAILURE && data->state != RESULT_FAILURE) { - eap_aka_state(data, data->use_result_ind ? - RESULT_SUCCESS : SUCCESS); - } - - data->num_id_req = 0; - data->num_notification = 0; - if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " - "fast reauths performed - force fullauth"); - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - } - os_free(decrypted); - return eap_aka_response_reauth(data, id, 0, data->nonce_s); -} - - -static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, - struct eap_method_ret *ret, - const struct wpabuf *reqData) -{ - struct eap_aka_data *data = priv; - const struct eap_hdr *req; - u8 subtype, id; - struct wpabuf *res; - const u8 *pos; - struct eap_sim_attrs attr; - size_t len; - - wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); - if (eap_get_config_identity(sm, &len) == NULL) { - wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); - eap_sm_request_identity(sm); - ret->ignore = TRUE; - return NULL; - } - - pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, - &len); - if (pos == NULL || len < 1) { - ret->ignore = TRUE; - return NULL; - } - req = wpabuf_head(reqData); - id = req->identifier; - len = be_to_host16(req->length); - - ret->ignore = FALSE; - ret->methodState = METHOD_MAY_CONT; - ret->decision = DECISION_FAIL; - ret->allowNotifications = TRUE; - - subtype = *pos++; - wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); - pos += 2; /* Reserved */ - - if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, - data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, - 0)) { - res = eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - goto done; - } - - switch (subtype) { - case EAP_AKA_SUBTYPE_IDENTITY: - res = eap_aka_process_identity(sm, data, id, reqData, &attr); - break; - case EAP_AKA_SUBTYPE_CHALLENGE: - res = eap_aka_process_challenge(sm, data, id, reqData, &attr); - break; - case EAP_AKA_SUBTYPE_NOTIFICATION: - res = eap_aka_process_notification(sm, data, id, reqData, - &attr); - break; - case EAP_AKA_SUBTYPE_REAUTHENTICATION: - res = eap_aka_process_reauthentication(sm, data, id, reqData, - &attr); - break; - case EAP_AKA_SUBTYPE_CLIENT_ERROR: - wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); - res = eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); - res = eap_aka_client_error(data, id, - EAP_AKA_UNABLE_TO_PROCESS_PACKET); - break; - } - -done: - if (data->state == FAILURE) { - ret->decision = DECISION_FAIL; - ret->methodState = METHOD_DONE; - } else if (data->state == SUCCESS) { - ret->decision = data->use_result_ind ? - DECISION_UNCOND_SUCC : DECISION_COND_SUCC; - /* - * It is possible for the server to reply with AKA - * Notification, so we must allow the method to continue and - * not only accept EAP-Success at this point. - */ - ret->methodState = data->use_result_ind ? - METHOD_DONE : METHOD_MAY_CONT; - } else if (data->state == RESULT_FAILURE) - ret->methodState = METHOD_CONT; - else if (data->state == RESULT_SUCCESS) - ret->methodState = METHOD_CONT; - - if (ret->methodState == METHOD_DONE) { - ret->allowNotifications = FALSE; - } - - return res; -} - - -static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - return data->pseudonym || data->reauth_id; -} - - -static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - eap_aka_clear_identities(data, CLEAR_EAP_ID); - data->prev_id = -1; - wpabuf_free(data->id_msgs); - data->id_msgs = NULL; - data->use_result_ind = 0; -} - - -static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - data->num_id_req = 0; - data->num_notification = 0; - eap_aka_state(data, CONTINUE); - return priv; -} - - -static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, - size_t *len) -{ - struct eap_aka_data *data = priv; - - if (data->reauth_id) { - *len = data->reauth_id_len; - return data->reauth_id; - } - - if (data->pseudonym) { - *len = data->pseudonym_len; - return data->pseudonym; - } - - return NULL; -} - - -static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - return data->state == SUCCESS; -} - - -static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_aka_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); - if (key == NULL) - return NULL; - - *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); - - return key; -} - - -static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_aka_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = os_malloc(EAP_EMSK_LEN); - if (key == NULL) - return NULL; - - *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); - - return key; -} - - -int eap_peer_aka_prime_register(void) -{ - struct eap_method *eap; - int ret; - - eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, - "AKA'"); - if (eap == NULL) - return -1; - - eap->init = eap_aka_prime_init; - eap->deinit = eap_aka_deinit; - eap->process = eap_aka_process; - eap->isKeyAvailable = eap_aka_isKeyAvailable; - eap->getKey = eap_aka_getKey; - eap->has_reauth_data = eap_aka_has_reauth_data; - eap->deinit_for_reauth = eap_aka_deinit_for_reauth; - eap->init_for_reauth = eap_aka_init_for_reauth; - eap->get_identity = eap_aka_get_identity; - eap->get_emsk = eap_aka_get_emsk; - - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - -#ifdef EAP_AKA_PRIME_BOTH - if (ret) - return ret; - - eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_AKA, - "AKA"); - if (eap == NULL) - return -1; - - eap->init = eap_aka_init; - eap->deinit = eap_aka_deinit; - eap->process = eap_aka_process; - eap->isKeyAvailable = eap_aka_isKeyAvailable; - eap->getKey = eap_aka_getKey; - eap->has_reauth_data = eap_aka_has_reauth_data; - eap->deinit_for_reauth = eap_aka_deinit_for_reauth; - eap->init_for_reauth = eap_aka_init_for_reauth; - eap->get_identity = eap_aka_get_identity; - eap->get_emsk = eap_aka_get_emsk; - - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); -#endif /* EAP_AKA_PRIME_BOTH */ - - return ret; -} diff --git a/src/eap_server/eap_aka.c b/src/eap_server/eap_aka.c index 3d54e730b..f9c4ebc7b 100644 --- a/src/eap_server/eap_aka.c +++ b/src/eap_server/eap_aka.c @@ -1,5 +1,5 @@ /* - * hostapd / EAP-AKA (RFC 4187) + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) * Copyright (c) 2005-2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify @@ -19,14 +19,16 @@ #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" #include "sha1.h" +#include "sha256.h" #include "crypto.h" struct eap_aka_data { u8 mk[EAP_SIM_MK_LEN]; u8 nonce_s[EAP_SIM_NONCE_S_LEN]; - u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN]; @@ -49,6 +51,10 @@ struct eap_aka_data { struct wpabuf *id_msgs; int pending_id; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; }; @@ -99,6 +105,9 @@ static void * eap_aka_init(struct eap_sm *sm) data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; + + data->eap_method = EAP_TYPE_AKA; + data->state = IDENTITY; eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; @@ -107,12 +116,48 @@ static void * eap_aka_init(struct eap_sm *sm) } +#ifdef EAP_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + /* TODO: make ANID configurable; see 3GPP TS 24.302 */ + char *network_name = "WLAN"; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA_PRIME; + data->network_name = os_malloc(os_strlen(network_name)); + if (data->network_name == NULL) { + os_free(data); + return NULL; + } + + data->network_name_len = os_strlen(network_name); + os_memcpy(data->network_name, network_name, data->network_name_len); + + data->state = IDENTITY; + eap_aka_determine_identity(sm, data, 1, 0); + data->pending_id = -1; + + return data; +} +#endif /* EAP_AKA_PRIME */ + + static void eap_aka_reset(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; os_free(data->next_pseudonym); os_free(data->next_reauth_id); wpabuf_free(data->id_msgs); + os_free(data->network_name); os_free(data); } @@ -141,7 +186,7 @@ static void eap_aka_add_checkcode(struct eap_aka_data *data, { const u8 *addr; size_t len; - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); @@ -158,10 +203,14 @@ static void eap_aka_add_checkcode(struct eap_aka_data *data, addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); - sha1_vector(1, &addr, &len, hash); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, - EAP_AKA_CHECKCODE_LEN); + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); } @@ -170,7 +219,8 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, { const u8 *addr; size_t len; - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; if (checkcode == NULL) return -1; @@ -185,7 +235,10 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, return 0; } - if (checkcode_len != EAP_AKA_CHECKCODE_LEN) { + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " "that AKA/Identity message were not used, but they " "were"); @@ -195,9 +248,12 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); - sha1_vector(1, &addr, &len, hash); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); - if (os_memcmp(hash, checkcode, EAP_AKA_CHECKCODE_LEN) != 0) { + if (os_memcmp(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } @@ -213,7 +269,7 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, sm->identity_len)) { @@ -309,11 +365,23 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_RAND"); eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (data->kdf) { + /* Add the selected KDF into the beginning */ + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, + NULL, 0); + } + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, + NULL, 0); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, + data->network_name_len, + data->network_name, data->network_name_len); + } if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { eap_sim_msg_free(msg); @@ -327,6 +395,35 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA) { + u16 flags = 0; + int i; + int aka_prime_preferred = 0; + + i = 0; + while (sm->user && 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) { + if (sm->user->methods[i].method == + EAP_TYPE_AKA) + break; + if (sm->user->methods[i].method == + EAP_TYPE_AKA_PRIME) { + aka_prime_preferred = 1; + break; + } + } + i++; + } + + if (aka_prime_preferred) + flags |= EAP_AKA_BIDDING_FLAG_D; + eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); + } +#endif /* EAP_AKA_PRIME */ + wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); @@ -345,13 +442,21 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, - data->emsk); - eap_sim_derive_keys_reauth(data->counter, sm->identity, - sm->identity_len, data->nonce_s, data->mk, - data->msk, data->emsk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + sm->identity, + sm->identity_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + eap_sim_derive_keys_reauth(data->counter, sm->identity, + sm->identity_len, data->nonce_s, + data->mk, data->msk, data->emsk); + } - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_REAUTHENTICATION); if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { @@ -379,7 +484,7 @@ static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, struct eap_sim_msg *msg; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_NOTIFICATION); wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, @@ -437,10 +542,12 @@ static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) static Boolean eap_aka_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { + struct eap_aka_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_AKA, respData, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); return TRUE; @@ -523,14 +630,33 @@ static void eap_aka_determine_identity(struct eap_sm *sm, data->reauth = eap_sim_db_get_reauth_entry( sm->eap_sim_db_priv, sm->identity, sm->identity_len); + if (data->reauth && + data->reauth->aka_prime != + (data->eap_method == EAP_TYPE_AKA_PRIME)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " + "was for different AKA version"); + data->reauth = NULL; + } if (data->reauth) { wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " "re-authentication"); identity = data->reauth->identity; identity_len = data->reauth->identity_len; data->counter = data->reauth->counter; - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + os_memcpy(data->k_encr, + data->reauth->k_encr, + EAP_SIM_K_ENCR_LEN); + os_memcpy(data->k_aut, + data->reauth->k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(data->k_re, + data->reauth->k_re, + EAP_AKA_PRIME_K_RE_LEN); + } else { + os_memcpy(data->mk, data->reauth->mk, + EAP_SIM_MK_LEN); + } } } } @@ -571,6 +697,17 @@ static void eap_aka_determine_identity(struct eap_sm *sm, return; } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^AK for CK',IK' derivation */ + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_AKA_PRIME */ + data->reauth = NULL; data->counter = 0; /* reset re-auth counter since this is full auth */ @@ -596,10 +733,16 @@ static void eap_aka_determine_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", sm->identity, identity_len); - eap_aka_derive_mk(sm->identity, identity_len, data->ik, data->ck, - data->mk); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, - data->emsk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(sm->identity, identity_len, data->ik, + data->ck, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } eap_aka_state(data, CHALLENGE); } @@ -638,6 +781,18 @@ static void eap_aka_process_identity(struct eap_sm *sm, } +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); +} + + static void eap_aka_process_challenge(struct eap_sm *sm, struct eap_aka_data *data, struct wpabuf *respData, @@ -648,6 +803,31 @@ static void eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); +#ifdef EAP_AKA_PRIME +#if 0 + /* KDF negotiation; to be enabled only after more than one KDF is + * supported */ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + attr->kdf_count == 1 && attr->mac == NULL) { + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { + wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " + "unknown KDF"); + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + data->kdf = attr->kdf[0]; + + /* Allow negotiation to continue with the selected KDF by + * sending another Challenge message */ + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + return; + } +#endif +#endif /* EAP_AKA_PRIME */ + if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, attr->checkcode_len)) { @@ -658,7 +838,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, return; } if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, attr->mac, NULL, 0)) { + eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "did not include valid AT_MAC"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; @@ -707,10 +887,21 @@ static void eap_aka_process_challenge(struct eap_sm *sm, data->next_pseudonym = NULL; } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, - data->next_reauth_id, data->counter + 1, - data->mk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->mk); + } data->next_reauth_id = NULL; } } @@ -763,7 +954,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, + eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, EAP_SIM_NONCE_S_LEN)) { wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " "did not include valid AT_MAC"); @@ -833,9 +1024,21 @@ static void eap_aka_process_reauth(struct eap_sm *sm, data->next_pseudonym = NULL; } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, data->next_reauth_id, - data->counter + 1, data->mk); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, + identity_len, + data->next_reauth_id, + data->counter + 1, + data->mk); + } data->next_reauth_id = NULL; } else { eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); @@ -898,7 +1101,8 @@ static void eap_aka_process(struct eap_sm *sm, void *priv, size_t len; struct eap_sim_attrs attr; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_AKA, respData, &len); + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); if (pos == NULL || len < 3) return; @@ -914,7 +1118,9 @@ static void eap_aka_process(struct eap_sm *sm, void *priv, return; } - if (eap_sim_parse_attr(pos, end, &attr, 1, 0)) { + if (eap_sim_parse_attr(pos, end, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, + 0)) { wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); @@ -1031,3 +1237,34 @@ int eap_server_aka_register(void) eap_server_method_free(eap); return ret; } + + +#ifdef EAP_AKA_PRIME +int eap_server_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->reset = eap_aka_reset; + eap->buildReq = eap_aka_buildReq; + eap->check = eap_aka_check; + eap->process = eap_aka_process; + eap->isDone = eap_aka_isDone; + eap->getKey = eap_aka_getKey; + eap->isSuccess = eap_aka_isSuccess; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + + return ret; +} +#endif /* EAP_AKA_PRIME */ diff --git a/src/eap_server/eap_aka_prime.c b/src/eap_server/eap_aka_prime.c deleted file mode 100644 index 19621323f..000000000 --- a/src/eap_server/eap_aka_prime.c +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * hostapd / EAP-AKA' (draft-arkko-eap-aka-kdf-10.txt) - * Copyright (c) 2005-2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eap_server/eap_i.h" -#include "eap_common/eap_sim_common.h" -#include "eap_server/eap_sim_db.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" - - -struct eap_aka_data { - u8 mk[EAP_SIM_MK_LEN]; - u8 nonce_s[EAP_SIM_NONCE_S_LEN]; - u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; - u8 k_encr[EAP_SIM_K_ENCR_LEN]; - u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; - u8 msk[EAP_SIM_KEYING_DATA_LEN]; - u8 emsk[EAP_EMSK_LEN]; - u8 rand[EAP_AKA_RAND_LEN]; - u8 autn[EAP_AKA_AUTN_LEN]; - u8 ck[EAP_AKA_CK_LEN]; - u8 ik[EAP_AKA_IK_LEN]; - u8 res[EAP_AKA_RES_MAX_LEN]; - size_t res_len; - enum { - IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE - } state; - char *next_pseudonym; - char *next_reauth_id; - u16 counter; - struct eap_sim_reauth *reauth; - int auts_reported; /* whether the current AUTS has been reported to the - * eap_sim_db */ - u16 notification; - int use_result_ind; - - struct wpabuf *id_msgs; - int pending_id; - u8 eap_method; - u8 *network_name; - size_t network_name_len; - u16 kdf; -}; - - -static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth); - - -static const char * eap_aka_state_txt(int state) -{ - switch (state) { - case IDENTITY: - return "IDENTITY"; - case CHALLENGE: - return "CHALLENGE"; - case REAUTH: - return "REAUTH"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - case NOTIFICATION: - return "NOTIFICATION"; - default: - return "Unknown?!"; - } -} - - -static void eap_aka_state(struct eap_aka_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA%s: %s -> %s", - data->eap_method == EAP_TYPE_AKA_PRIME ? "'" : "", - eap_aka_state_txt(data->state), - eap_aka_state_txt(state)); - data->state = state; -} - - -#ifdef EAP_AKA_PRIME_BOTH -static void * eap_aka_init(struct eap_sm *sm) -{ - struct eap_aka_data *data; - - if (sm->eap_sim_db_priv == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); - return NULL; - } - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - - data->eap_method = EAP_TYPE_AKA; - - data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); - data->pending_id = -1; - - return data; -} -#endif /* EAP_AKA_PRIME_BOTH */ - - -static void * eap_aka_prime_init(struct eap_sm *sm) -{ - struct eap_aka_data *data; - /* TODO: make ANID configurable; see 3GPP TS 24.302 */ - char *network_name = "WLAN"; - - if (sm->eap_sim_db_priv == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); - return NULL; - } - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - - data->eap_method = EAP_TYPE_AKA_PRIME; - data->network_name = os_malloc(os_strlen(network_name)); - if (data->network_name == NULL) { - os_free(data); - return NULL; - } - - data->network_name_len = os_strlen(network_name); - os_memcpy(data->network_name, network_name, data->network_name_len); - - data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); - data->pending_id = -1; - - return data; -} - - -static void eap_aka_reset(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - os_free(data->next_pseudonym); - os_free(data->next_reauth_id); - wpabuf_free(data->id_msgs); - os_free(data->network_name); - os_free(data); -} - - -static int eap_aka_add_id_msg(struct eap_aka_data *data, - const struct wpabuf *msg) -{ - if (msg == NULL) - return -1; - - if (data->id_msgs == NULL) { - data->id_msgs = wpabuf_dup(msg); - return data->id_msgs == NULL ? -1 : 0; - } - - if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) - return -1; - wpabuf_put_buf(data->id_msgs, msg); - - return 0; -} - - -static void eap_aka_add_checkcode(struct eap_aka_data *data, - struct eap_sim_msg *msg) -{ - const u8 *addr; - size_t len; - u8 hash[SHA256_MAC_LEN]; - - wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); - - if (data->id_msgs == NULL) { - /* - * No EAP-AKA/Identity packets were exchanged - send empty - * checkcode. - */ - eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); - return; - } - - /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ - addr = wpabuf_head(data->id_msgs); - len = wpabuf_len(data->id_msgs); - wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); - if (data->eap_method == EAP_TYPE_AKA_PRIME) - sha256_vector(1, &addr, &len, hash); - else - sha1_vector(1, &addr, &len, hash); - - eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, - data->eap_method == EAP_TYPE_AKA_PRIME ? - EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); -} - - -static int eap_aka_verify_checkcode(struct eap_aka_data *data, - const u8 *checkcode, size_t checkcode_len) -{ - const u8 *addr; - size_t len; - u8 hash[SHA256_MAC_LEN]; - size_t hash_len; - - if (checkcode == NULL) - return -1; - - if (data->id_msgs == NULL) { - if (checkcode_len != 0) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " - "indicates that AKA/Identity messages were " - "used, but they were not"); - return -1; - } - return 0; - } - - hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? - EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; - - if (checkcode_len != hash_len) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " - "that AKA/Identity message were not used, but they " - "were"); - return -1; - } - - /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ - addr = wpabuf_head(data->id_msgs); - len = wpabuf_len(data->id_msgs); - if (data->eap_method == EAP_TYPE_AKA_PRIME) - sha256_vector(1, &addr, &len, hash); - else - sha1_vector(1, &addr, &len, hash); - - if (os_memcmp(hash, checkcode, hash_len) != 0) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); - return -1; - } - - return 0; -} - - -static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, - struct eap_aka_data *data, u8 id) -{ - struct eap_sim_msg *msg; - struct wpabuf *buf; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, - EAP_AKA_SUBTYPE_IDENTITY); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } else { - /* - * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is - * ignored and the AKA/Identity is used to request the - * identity. - */ - wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); - } - buf = eap_sim_msg_finish(msg, NULL, NULL, 0); - if (eap_aka_add_id_msg(data, buf) < 0) { - wpabuf_free(buf); - return NULL; - } - data->pending_id = id; - return buf; -} - - -static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, - struct eap_sim_msg *msg, u16 counter, - const u8 *nonce_s) -{ - os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); - os_free(data->next_reauth_id); - if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { - data->next_reauth_id = - eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); - } else { - wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " - "count exceeded - force full authentication"); - data->next_reauth_id = NULL; - } - - if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && - counter == 0 && nonce_s == NULL) - return 0; - - wpa_printf(MSG_DEBUG, " AT_IV"); - wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); - eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); - - if (counter > 0) { - wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); - eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); - } - - if (nonce_s) { - wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); - eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, - EAP_SIM_NONCE_S_LEN); - } - - if (data->next_pseudonym) { - wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", - data->next_pseudonym); - eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, - os_strlen(data->next_pseudonym), - (u8 *) data->next_pseudonym, - os_strlen(data->next_pseudonym)); - } - - if (data->next_reauth_id) { - wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", - data->next_reauth_id); - eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, - os_strlen(data->next_reauth_id), - (u8 *) data->next_reauth_id, - os_strlen(data->next_reauth_id)); - } - - if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " - "AT_ENCR_DATA"); - return -1; - } - - return 0; -} - - -static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, - struct eap_aka_data *data, - u8 id) -{ - struct eap_sim_msg *msg; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, - EAP_AKA_SUBTYPE_CHALLENGE); - wpa_printf(MSG_DEBUG, " AT_RAND"); - eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); - eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - if (data->kdf) { - /* Add the selected KDF into the beginning */ - eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, - NULL, 0); - } - eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, - NULL, 0); - eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, - data->network_name_len, - data->network_name, data->network_name_len); - } - - if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { - eap_sim_msg_free(msg); - return NULL; - } - - eap_aka_add_checkcode(data, msg); - - if (sm->eap_sim_aka_result_ind) { - wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); - eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); - } - -#ifdef EAP_AKA_PRIME - if (data->eap_method == EAP_TYPE_AKA) { - u16 flags = 0; - int i; - int aka_prime_preferred = 0; - - i = 0; - while (sm->user && 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) { - if (sm->user->methods[i].method == - EAP_TYPE_AKA) - break; - if (sm->user->methods[i].method == - EAP_TYPE_AKA_PRIME) { - aka_prime_preferred = 1; - break; - } - } - i++; - } - - if (aka_prime_preferred) - flags |= EAP_AKA_BIDDING_FLAG_D; - eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); - } -#endif /* EAP_AKA_PRIME */ - - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); -} - - -static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, - struct eap_aka_data *data, u8 id) -{ - struct eap_sim_msg *msg; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); - - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) - return NULL; - wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", - data->nonce_s, EAP_SIM_NONCE_S_LEN); - - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, - sm->identity, - sm->identity_len, - data->nonce_s, - data->msk, data->emsk); - } else { - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, - data->msk, data->emsk); - eap_sim_derive_keys_reauth(data->counter, sm->identity, - sm->identity_len, data->nonce_s, - data->mk, data->msk, data->emsk); - } - - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, - EAP_AKA_SUBTYPE_REAUTHENTICATION); - - if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { - eap_sim_msg_free(msg); - return NULL; - } - - eap_aka_add_checkcode(data, msg); - - if (sm->eap_sim_aka_result_ind) { - wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); - eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); - } - - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); -} - - -static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, - struct eap_aka_data *data, - u8 id) -{ - struct eap_sim_msg *msg; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, - EAP_AKA_SUBTYPE_NOTIFICATION); - wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); - eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, - NULL, 0); - if (data->use_result_ind) { - if (data->reauth) { - wpa_printf(MSG_DEBUG, " AT_IV"); - wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); - eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, - EAP_SIM_AT_ENCR_DATA); - wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", - data->counter); - eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, - NULL, 0); - - if (eap_sim_msg_add_encr_end(msg, data->k_encr, - EAP_SIM_AT_PADDING)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " - "encrypt AT_ENCR_DATA"); - eap_sim_msg_free(msg); - return NULL; - } - } - - wpa_printf(MSG_DEBUG, " AT_MAC"); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - } - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); -} - - -static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) -{ - struct eap_aka_data *data = priv; - - data->auts_reported = 0; - switch (data->state) { - case IDENTITY: - return eap_aka_build_identity(sm, data, id); - case CHALLENGE: - return eap_aka_build_challenge(sm, data, id); - case REAUTH: - return eap_aka_build_reauth(sm, data, id); - case NOTIFICATION: - return eap_aka_build_notification(sm, data, id); - default: - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " - "buildReq", data->state); - break; - } - return NULL; -} - - -static Boolean eap_aka_check(struct eap_sm *sm, void *priv, - struct wpabuf *respData) -{ - struct eap_aka_data *data = priv; - const u8 *pos; - size_t len; - - pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, - &len); - if (pos == NULL || len < 3) { - wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) -{ - if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || - subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) - return FALSE; - - switch (data->state) { - case IDENTITY: - if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { - wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - case CHALLENGE: - if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && - subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { - wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - case REAUTH: - if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { - wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - case NOTIFICATION: - if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { - wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - default: - wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " - "processing a response", data->state); - return TRUE; - } - - return FALSE; -} - - -static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth) -{ - const u8 *identity; - size_t identity_len; - int res; - - identity = NULL; - identity_len = 0; - - if (after_reauth && data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { - identity = sm->identity; - identity_len = sm->identity_len; - } else { - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, - sm->identity, - sm->identity_len, - &identity_len); - if (identity == NULL) { - data->reauth = eap_sim_db_get_reauth_entry( - sm->eap_sim_db_priv, sm->identity, - sm->identity_len); - if (data->reauth && - data->reauth->aka_prime != - (data->eap_method == EAP_TYPE_AKA_PRIME)) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " - "was for different AKA version"); - data->reauth = NULL; - } - if (data->reauth) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " - "re-authentication"); - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - data->counter = data->reauth->counter; - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - os_memcpy(data->k_encr, - data->reauth->k_encr, - EAP_SIM_K_ENCR_LEN); - os_memcpy(data->k_aut, - data->reauth->k_aut, - EAP_AKA_PRIME_K_AUT_LEN); - os_memcpy(data->k_re, - data->reauth->k_re, - EAP_AKA_PRIME_K_RE_LEN); - } else { - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); - } - } - } - } - - if (identity == NULL || - eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len) < 0) { - if (before_identity) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " - "not known - send AKA-Identity request"); - eap_aka_state(data, IDENTITY); - return; - } else { - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " - "permanent user name is known; try to use " - "it"); - /* eap_sim_db_get_aka_auth() will report failure, if - * this identity is not known. */ - } - } - - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", - identity, identity_len); - - if (!after_reauth && data->reauth) { - eap_aka_state(data, REAUTH); - return; - } - - res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, - identity_len, data->rand, data->autn, - data->ik, data->ck, data->res, - &data->res_len, sm); - if (res == EAP_SIM_DB_PENDING) { - wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " - "not yet available - pending request"); - sm->method_pending = METHOD_PENDING_WAIT; - return; - } - -#ifdef EAP_AKA_PRIME - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the - * needed 6-octet SQN ^AK for CK',IK' derivation */ - eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, - data->autn, - data->network_name, - data->network_name_len); - } -#endif /* EAP_AKA_PRIME */ - - data->reauth = NULL; - data->counter = 0; /* reset re-auth counter since this is full auth */ - - if (res != 0) { - wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " - "authentication data for the peer"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - if (sm->method_pending == METHOD_PENDING_WAIT) { - wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " - "available - abort pending wait"); - sm->method_pending = METHOD_PENDING_NONE; - } - - identity_len = sm->identity_len; - while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { - wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " - "character from identity"); - identity_len--; - } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", - sm->identity, identity_len); - - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys(identity, identity_len, data->ik, - data->ck, data->k_encr, data->k_aut, - data->k_re, data->msk, data->emsk); - } else { - eap_aka_derive_mk(sm->identity, identity_len, data->ik, - data->ck, data->mk); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, - data->msk, data->emsk); - } - - eap_aka_state(data, CHALLENGE); -} - - -static void eap_aka_process_identity(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); - - if (attr->mac || attr->iv || attr->encr_data) { - wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " - "received in EAP-Response/AKA-Identity"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - if (attr->identity) { - os_free(sm->identity); - sm->identity = os_malloc(attr->identity_len); - if (sm->identity) { - os_memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } - } - - eap_aka_determine_identity(sm, data, 0, 0); - if (eap_get_id(respData) == data->pending_id) { - data->pending_id = -1; - eap_aka_add_id_msg(data, respData); - } -} - - -static int eap_aka_verify_mac(struct eap_aka_data *data, - const struct wpabuf *req, - const u8 *mac, const u8 *extra, - size_t extra_len) -{ - if (data->eap_method == EAP_TYPE_AKA_PRIME) - return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, - extra_len); - return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); -} - - -static void eap_aka_process_challenge(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - const u8 *identity; - size_t identity_len; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); - -#if 0 - /* KDF negotiation; to be enabled only after more than one KDF is - * supported */ - if (data->eap_method == EAP_TYPE_AKA_PRIME && - attr->kdf_count == 1 && attr->mac == NULL) { - if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { - wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " - "unknown KDF"); - data->notification = - EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - data->kdf = attr->kdf[0]; - - /* Allow negotiation to continue with the selected KDF by - * sending another Challenge message */ - wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); - return; - } -#endif - - if (attr->checkcode && - eap_aka_verify_checkcode(data, attr->checkcode, - attr->checkcode_len)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " - "message"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - if (attr->mac == NULL || - eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " - "did not include valid AT_MAC"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - /* - * AT_RES is padded, so verify that there is enough room for RES and - * that the RES length in bits matches with the expected RES. - */ - if (attr->res == NULL || attr->res_len < data->res_len || - attr->res_len_bits != data->res_len * 8 || - os_memcmp(attr->res, data->res, data->res_len) != 0) { - wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " - "include valid AT_RES (attr len=%lu, res len=%lu " - "bits, expected %lu bits)", - (unsigned long) attr->res_len, - (unsigned long) attr->res_len_bits, - (unsigned long) data->res_len * 8); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " - "correct AT_MAC"); - if (sm->eap_sim_aka_result_ind && attr->result_ind) { - data->use_result_ind = 1; - data->notification = EAP_SIM_SUCCESS; - eap_aka_state(data, NOTIFICATION); - } else - eap_aka_state(data, SUCCESS); - - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, &identity_len); - if (identity == NULL) { - identity = sm->identity; - identity_len = sm->identity_len; - } - - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, - data->next_pseudonym); - data->next_pseudonym = NULL; - } - if (data->next_reauth_id) { - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, - data->next_reauth_id, - data->counter + 1, - data->k_encr, data->k_aut, - data->k_re); - } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, - data->next_reauth_id, - data->counter + 1, - data->mk); - } - data->next_reauth_id = NULL; - } -} - - -static void eap_aka_process_sync_failure(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); - - if (attr->auts == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " - "message did not include valid AT_AUTS"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - /* Avoid re-reporting AUTS when processing pending EAP packet by - * maintaining a local flag stating whether this AUTS has already been - * reported. */ - if (!data->auts_reported && - eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, attr->auts, - data->rand)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - data->auts_reported = 1; - - /* Try again after resynchronization */ - eap_aka_determine_identity(sm, data, 0, 0); -} - - -static void eap_aka_process_reauth(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - struct eap_sim_attrs eattr; - u8 *decrypted = NULL; - const u8 *identity, *id2; - size_t identity_len, id2_len; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); - - if (attr->mac == NULL || - eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, - EAP_SIM_NONCE_S_LEN)) { - wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " - "did not include valid AT_MAC"); - goto fail; - } - - if (attr->encr_data == NULL || attr->iv == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " - "message did not include encrypted data"); - goto fail; - } - - decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, - attr->encr_data_len, attr->iv, &eattr, - 0); - if (decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " - "data from reauthentication message"); - goto fail; - } - - if (eattr.counter != data->counter) { - wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " - "used incorrect counter %u, expected %u", - eattr.counter, data->counter); - goto fail; - } - os_free(decrypted); - decrypted = NULL; - - wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " - "the correct AT_MAC"); - - if (eattr.counter_too_small) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " - "included AT_COUNTER_TOO_SMALL - starting full " - "authentication"); - eap_aka_determine_identity(sm, data, 0, 1); - return; - } - - if (sm->eap_sim_aka_result_ind && attr->result_ind) { - data->use_result_ind = 1; - data->notification = EAP_SIM_SUCCESS; - eap_aka_state(data, NOTIFICATION); - } else - eap_aka_state(data, SUCCESS); - - if (data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else { - identity = sm->identity; - identity_len = sm->identity_len; - } - - id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, - identity_len, &id2_len); - if (id2) { - identity = id2; - identity_len = id2_len; - } - - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } - if (data->next_reauth_id) { - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, - data->next_reauth_id, - data->counter + 1, - data->k_encr, data->k_aut, - data->k_re); - } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, - data->next_reauth_id, - data->counter + 1, - data->mk); - } - data->next_reauth_id = NULL; - } else { - eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); - data->reauth = NULL; - } - - return; - -fail: - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); - data->reauth = NULL; - os_free(decrypted); -} - - -static void eap_aka_process_client_error(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", - attr->client_error_code); - if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) - eap_aka_state(data, SUCCESS); - else - eap_aka_state(data, FAILURE); -} - - -static void eap_aka_process_authentication_reject( - struct eap_sm *sm, struct eap_aka_data *data, - struct wpabuf *respData, struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); - eap_aka_state(data, FAILURE); -} - - -static void eap_aka_process_notification(struct eap_sm *sm, - struct eap_aka_data *data, - struct wpabuf *respData, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); - if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) - eap_aka_state(data, SUCCESS); - else - eap_aka_state(data, FAILURE); -} - - -static void eap_aka_process(struct eap_sm *sm, void *priv, - struct wpabuf *respData) -{ - struct eap_aka_data *data = priv; - const u8 *pos, *end; - u8 subtype; - size_t len; - struct eap_sim_attrs attr; - - pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, - &len); - if (pos == NULL || len < 3) - return; - - end = pos + len; - subtype = *pos; - pos += 3; - - if (eap_aka_subtype_ok(data, subtype)) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " - "EAP-AKA Subtype in EAP Response"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - if (eap_sim_parse_attr(pos, end, &attr, - data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, - 0)) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } - - if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { - eap_aka_process_client_error(sm, data, respData, &attr); - return; - } - - if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { - eap_aka_process_authentication_reject(sm, data, respData, - &attr); - return; - } - - switch (data->state) { - case IDENTITY: - eap_aka_process_identity(sm, data, respData, &attr); - break; - case CHALLENGE: - if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { - eap_aka_process_sync_failure(sm, data, respData, - &attr); - } else { - eap_aka_process_challenge(sm, data, respData, &attr); - } - break; - case REAUTH: - eap_aka_process_reauth(sm, data, respData, &attr); - break; - case NOTIFICATION: - eap_aka_process_notification(sm, data, respData, &attr); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " - "process", data->state); - break; - } -} - - -static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_aka_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); - if (key == NULL) - return NULL; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); - *len = EAP_SIM_KEYING_DATA_LEN; - return key; -} - - -static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_aka_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = os_malloc(EAP_EMSK_LEN); - if (key == NULL) - return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); - *len = EAP_EMSK_LEN; - return key; -} - - -static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_aka_data *data = priv; - return data->state == SUCCESS; -} - - -int eap_server_aka_prime_register(void) -{ - struct eap_method *eap; - int ret; - - eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, - "AKA'"); - if (eap == NULL) - return -1; - - eap->init = eap_aka_prime_init; - eap->reset = eap_aka_reset; - eap->buildReq = eap_aka_buildReq; - eap->check = eap_aka_check; - eap->process = eap_aka_process; - eap->isDone = eap_aka_isDone; - eap->getKey = eap_aka_getKey; - eap->isSuccess = eap_aka_isSuccess; - eap->get_emsk = eap_aka_get_emsk; - - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - -#ifdef EAP_AKA_PRIME_BOTH - if (ret) - return ret; - - eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_AKA, - "AKA"); - if (eap == NULL) - return -1; - - eap->init = eap_aka_init; - eap->reset = eap_aka_reset; - eap->buildReq = eap_aka_buildReq; - eap->check = eap_aka_check; - eap->process = eap_aka_process; - eap->isDone = eap_aka_isDone; - eap->getKey = eap_aka_getKey; - eap->isSuccess = eap_aka_isSuccess; - eap->get_emsk = eap_aka_get_emsk; - - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); -#endif /* EAP_AKA_PRIME_BOTH */ - - return ret; -} diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index 0c5ee9a80..34dc8e442 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -10,6 +10,8 @@ ChangeLog for wpa_supplicant wpa_supplicant.conf (see WPS section in the example configuration file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to manage WPS negotiation; see README-WPS for more details + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + 2008-11-23 - v0.6.6 * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA (can be used to simulate test SIM/USIM card with a known private key; diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index ff069a8f4..02b28f605 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -431,14 +431,9 @@ ifdef CONFIG_EAP_AKA_PRIME # EAP-AKA' ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) CFLAGS += -DEAP_AKA_PRIME_DYNAMIC -EAPDYN += ../src/eap_peer/eap_aka_prime.so else CFLAGS += -DEAP_AKA_PRIME -OBJS += ../src/eap_peer/eap_aka_prime.o -OBJS_h += ../src/eap_server/eap_aka_prime.o endif -CONFIG_IEEE8021X_EAPOL=y -CONFIG_EAP_SIM_COMMON=y endif ifdef CONFIG_EAP_SIM_COMMON diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index e62b9b3df..a62373610 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -156,6 +156,10 @@ CONFIG_EAP_LEAP=y # EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) #CONFIG_EAP_AKA=y +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + # Enable USIM simulator (Milenage) for EAP-AKA #CONFIG_USIM_SIMULATOR=y diff --git a/wpa_supplicant/eap_testing.txt b/wpa_supplicant/eap_testing.txt index cbf1c4ec9..34c9fb870 100644 --- a/wpa_supplicant/eap_testing.txt +++ b/wpa_supplicant/eap_testing.txt @@ -94,6 +94,7 @@ EAP-TTLS/EAP-GPSK - - - - - - - - - - + - EAP-TTLS + TNC - - - - - + - - - - + - EAP-SIM + - - ? - + - ? - - + - EAP-AKA - - - - - + - - - - + - +EAP-AKA' - - - - - - - - - - + - EAP-PSK +7 - - - - + - - - - + - EAP-PAX - - - - - + - - - - + - EAP-SAKE - - - - - - - - - - + - diff --git a/www/hostapd/index.html b/www/hostapd/index.html index 4e953d5d7..8624e3b76 100644 --- a/www/hostapd/index.html +++ b/www/hostapd/index.html @@ -53,6 +53,7 @@ RADIUS backend authentication server) ("WPA-Enterprise")
  • EAP-TTLS/CHAP
  • EAP-SIM
  • EAP-AKA
  • +
  • EAP-AKA'
  • EAP-PAX
  • EAP-PSK
  • EAP-SAKE
  • @@ -266,7 +267,7 @@ Internet Systems Consortium (ISC).
    Jouni Malinen
    -Last modified: Fri Nov 28 18:27:51 EET 2008 +Last modified: Sun Dec 7 19:24:25 EET 2008 diff --git a/www/wpa_supplicant/index.html b/www/wpa_supplicant/index.html index e01a6c4c7..cda18f676 100644 --- a/www/wpa_supplicant/index.html +++ b/www/wpa_supplicant/index.html @@ -63,6 +63,7 @@ build for x86 target).

  • EAP-TTLS/CHAP
  • EAP-SIM
  • EAP-AKA
  • +
  • EAP-AKA'
  • EAP-PSK
  • EAP-FAST
  • EAP-PAX
  • @@ -356,7 +357,7 @@ Internet Systems Consortium (ISC).
    Jouni Malinen
    -Last modified: Fri Nov 28 18:27:42 EET 2008 +Last modified: Sun Dec 7 19:24:16 EET 2008