EAP-AKA': Added processing of AT_KDF and AT_KDF_INPUT attributes

Network Name is not yet generated and validated based on 3GPP.33.402
(i.e., a hardcoded string is used in server and anything is accepted in
peer).
This commit is contained in:
Jouni Malinen 2008-12-04 20:29:46 +02:00
parent b8ab624984
commit 6ec4021c03
4 changed files with 223 additions and 2 deletions

View file

@ -788,6 +788,52 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end,
wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
attr->result_ind = 1; attr->result_ind = 1;
break; break;
#ifdef EAP_AKA_PRIME
case EAP_SIM_AT_KDF_INPUT:
if (aka != 2) {
wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
"AT_KDF_INPUT");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
plen = WPA_GET_BE16(apos);
apos += 2;
alen -= 2;
if (plen > alen) {
wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
"AT_KDF_INPUT (Actual Length %lu, "
"remaining length %lu)",
(unsigned long) plen,
(unsigned long) alen);
return -1;
}
attr->kdf_input = apos;
attr->kdf_input_len = plen;
break;
case EAP_SIM_AT_KDF:
if (aka != 2) {
wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
"AT_KDF");
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
if (alen != 2) {
wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
"AT_KDF (len %lu)",
(unsigned long) alen);
return -1;
}
if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
"AT_KDF attributes - ignore this");
continue;
}
attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
attr->kdf_count++;
break;
#endif /* EAP_AKA_PRIME */
default: default:
if (pos[0] < 128) { if (pos[0] < 128) {
wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "

View file

@ -126,6 +126,8 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ #define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ #define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ #define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
#define EAP_SIM_AT_KDF 24 /* only AKA' */
#define EAP_SIM_AT_IV 129 #define EAP_SIM_AT_IV 129
#define EAP_SIM_AT_ENCR_DATA 130 #define EAP_SIM_AT_ENCR_DATA 130
#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ #define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
@ -140,6 +142,8 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 #define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
#define EAP_SIM_SUCCESS 32768 #define EAP_SIM_SUCCESS 32768
/* EAP-AKA' AT_KDF Key Derivation Function values */
#define EAP_AKA_PRIME_KDF 1
enum eap_sim_id_req { enum eap_sim_id_req {
NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
@ -151,14 +155,19 @@ struct eap_sim_attrs {
const u8 *next_pseudonym, *next_reauth_id; const u8 *next_pseudonym, *next_reauth_id;
const u8 *nonce_mt, *identity, *res, *auts; const u8 *nonce_mt, *identity, *res, *auts;
const u8 *checkcode; const u8 *checkcode;
const u8 *kdf_input;
size_t num_chal, version_list_len, encr_data_len; size_t num_chal, version_list_len, encr_data_len;
size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len; size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
size_t res_len_bits; size_t res_len_bits;
size_t checkcode_len; size_t checkcode_len;
size_t kdf_input_len;
enum eap_sim_id_req id_req; enum eap_sim_id_req id_req;
int notification, counter, selected_version, client_error_code; int notification, counter, selected_version, client_error_code;
int counter_too_small; int counter_too_small;
int result_ind; int result_ind;
#define EAP_AKA_PRIME_KDF_MAX 10
u16 kdf[EAP_AKA_PRIME_KDF_MAX];
size_t kdf_count;
}; };
int eap_sim_parse_attr(const u8 *start, const u8 *end, int eap_sim_parse_attr(const u8 *start, const u8 *end,

View file

@ -57,6 +57,9 @@ struct eap_aka_data {
int prev_id; int prev_id;
int result_ind, use_result_ind; int result_ind, use_result_ind;
u8 eap_method; u8 eap_method;
u8 *network_name;
size_t network_name_len;
u16 kdf;
}; };
@ -129,6 +132,7 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv)
os_free(data->reauth_id); os_free(data->reauth_id);
os_free(data->last_eap_identity); os_free(data->last_eap_identity);
wpabuf_free(data->id_msgs); wpabuf_free(data->id_msgs);
os_free(data->network_name);
os_free(data); os_free(data);
} }
} }
@ -650,6 +654,89 @@ static int eap_aka_verify_mac(struct eap_aka_data *data,
} }
#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, static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
struct eap_aka_data *data, struct eap_aka_data *data,
u8 id, u8 id,
@ -672,6 +759,40 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
EAP_AKA_UNABLE_TO_PROCESS_PACKET); 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);
}
#endif /* EAP_AKA_PRIME */
data->reauth = 0; data->reauth = 0;
if (!attr->mac || !attr->rand || !attr->autn) { if (!attr->mac || !attr->rand || !attr->autn) {
wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
@ -1020,7 +1141,8 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
pos += 2; /* Reserved */ 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)) { 0)) {
res = eap_aka_client_error(data, id, res = eap_aka_client_error(data, id,
EAP_AKA_UNABLE_TO_PROCESS_PACKET); EAP_AKA_UNABLE_TO_PROCESS_PACKET);

View file

@ -52,6 +52,9 @@ struct eap_aka_data {
struct wpabuf *id_msgs; struct wpabuf *id_msgs;
int pending_id; int pending_id;
u8 eap_method; u8 eap_method;
u8 *network_name;
size_t network_name_len;
u16 kdf;
}; };
@ -130,6 +133,13 @@ static void * eap_aka_prime_init(struct eap_sm *sm)
return NULL; return NULL;
data->eap_method = EAP_TYPE_AKA_PRIME; data->eap_method = EAP_TYPE_AKA_PRIME;
data->network_name = os_malloc(3);
if (data->network_name == NULL) {
os_free(data);
return NULL;
}
os_memcpy(data->network_name, "FOO", 3); /* FIX: 3GPP.24.302 */
data->network_name_len = 3;
data->state = IDENTITY; data->state = IDENTITY;
eap_aka_determine_identity(sm, data, 1, 0); eap_aka_determine_identity(sm, data, 1, 0);
@ -145,6 +155,7 @@ static void eap_aka_reset(struct eap_sm *sm, void *priv)
os_free(data->next_pseudonym); os_free(data->next_pseudonym);
os_free(data->next_reauth_id); os_free(data->next_reauth_id);
wpabuf_free(data->id_msgs); wpabuf_free(data->id_msgs);
os_free(data->network_name);
os_free(data); os_free(data);
} }
@ -357,6 +368,18 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, " AT_RAND"); 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_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_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)) { if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
eap_sim_msg_free(msg); eap_sim_msg_free(msg);
@ -738,6 +761,25 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
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;
}
if (attr->checkcode && if (attr->checkcode &&
eap_aka_verify_checkcode(data, attr->checkcode, eap_aka_verify_checkcode(data, attr->checkcode,
attr->checkcode_len)) { attr->checkcode_len)) {
@ -1028,7 +1070,9 @@ static void eap_aka_process(struct eap_sm *sm, void *priv,
return; 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"); wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
eap_aka_state(data, NOTIFICATION); eap_aka_state(data, NOTIFICATION);