EAP-FAST: Cleaned up TLV processing and added support for EAP Sequences

Number of TLVs were processed in groups and these cases were now separated
into more flexible processing of one TLV at the time. wpabuf_concat()
function was added to make it easier to concatenate TLVs. EAP Sequences are
now supported in both server and peer code, but the server side is not
enabled by default.
This commit is contained in:
Jouni Malinen 2008-02-27 17:59:34 -08:00
parent 2bab8ae401
commit 7914585fe0
6 changed files with 189 additions and 105 deletions

View file

@ -3,6 +3,7 @@ ChangeLog for hostapd
????-??-?? - v0.6.4 ????-??-?? - v0.6.4
* added peer identity into EAP-FAST PAC-Opaque and skip Phase 2 * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2
Identity Request if identity is already known Identity Request if identity is already known
* added support for EAP Sequences in EAP-FAST Phase 2
2008-02-22 - v0.6.3 2008-02-22 - v0.6.3
* fixed Reassociation Response callback processing when using internal * fixed Reassociation Response callback processing when using internal

View file

@ -706,17 +706,16 @@ static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
static struct wpabuf * eap_fast_process_crypto_binding( static struct wpabuf * eap_fast_process_crypto_binding(
struct eap_sm *sm, struct eap_fast_data *data, struct eap_sm *sm, struct eap_fast_data *data,
struct eap_method_ret *ret, struct eap_method_ret *ret,
struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len, int final) struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
{ {
struct wpabuf *resp; struct wpabuf *resp;
u8 *pos; u8 *pos;
struct eap_tlv_intermediate_result_tlv *rresult;
u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN]; u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
int res, req_tunnel_pac = 0; int res;
size_t len; size_t len;
if (eap_fast_validate_crypto_binding(_bind) < 0) if (eap_fast_validate_crypto_binding(_bind) < 0)
return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); return NULL;
if (eap_fast_get_cmk(sm, data, cmk) < 0) if (eap_fast_get_cmk(sm, data, cmk) < 0)
return NULL; return NULL;
@ -735,9 +734,8 @@ static struct wpabuf * eap_fast_process_crypto_binding(
_bind->compound_mac, sizeof(cmac)); _bind->compound_mac, sizeof(cmac));
if (res != 0) { if (res != 0) {
wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
os_memcpy(_bind->compound_mac, cmac, sizeof(cmac)); os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
return resp; return NULL;
} }
/* /*
@ -745,73 +743,25 @@ static struct wpabuf * eap_fast_process_crypto_binding(
* crypto binding to allow server to complete authentication. * crypto binding to allow server to complete authentication.
*/ */
if (data->current_pac == NULL && data->provisioning && len = sizeof(struct eap_tlv_crypto_binding_tlv);
!data->anon_provisioning) {
/*
* Need to request Tunnel PAC when using authenticated
* provisioning.
*/
wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
req_tunnel_pac = 1;
}
len = sizeof(*rresult) + sizeof(struct eap_tlv_crypto_binding_tlv);
if (req_tunnel_pac)
len += sizeof(struct eap_tlv_hdr) +
sizeof(struct eap_tlv_request_action_tlv) +
sizeof(struct eap_tlv_pac_type_tlv);
resp = wpabuf_alloc(len); resp = wpabuf_alloc(len);
if (resp == NULL) if (resp == NULL)
return NULL; return NULL;
/*
* Both intermediate and final Result TLVs are identical, so ok to use
* the same structure definition for them.
*/
rresult = wpabuf_put(resp, sizeof(*rresult));
rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
(final ? EAP_TLV_RESULT_TLV :
EAP_TLV_INTERMEDIATE_RESULT_TLV));
rresult->length = host_to_be16(2);
rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
if (!data->anon_provisioning && data->phase2_success && if (!data->anon_provisioning && data->phase2_success &&
eap_fast_derive_msk(data) < 0) { eap_fast_derive_msk(data) < 0) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
ret->methodState = METHOD_DONE; ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL; ret->decision = DECISION_FAIL;
rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE);
data->phase2_success = 0; data->phase2_success = 0;
wpabuf_free(resp);
return NULL;
} }
pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
pos, _bind, cmk); pos, _bind, cmk);
if (req_tunnel_pac) {
u8 *pos2;
pos = wpabuf_put(resp, 0);
pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
wpabuf_put(resp, pos2 - pos);
}
if (final && data->phase2_success) {
if (data->anon_provisioning) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
"provisioning completed successfully.");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
} else {
wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
"completed successfully.");
if (data->provisioning)
ret->methodState = METHOD_MAY_CONT;
else
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
}
}
return resp; return resp;
} }
@ -1028,7 +978,7 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
os_memset(&entry, 0, sizeof(entry)); os_memset(&entry, 0, sizeof(entry));
if (eap_fast_process_pac_tlv(&entry, pac, pac_len) || if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
eap_fast_process_pac_info(&entry)) eap_fast_process_pac_info(&entry))
return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); return NULL;
eap_fast_add_pac(&data->pac, &data->current_pac, &entry); eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
@ -1140,6 +1090,24 @@ static int eap_fast_encrypt_response(struct eap_sm *sm,
} }
static struct wpabuf * eap_fast_pac_request(void)
{
struct wpabuf *tmp;
u8 *pos, *pos2;
tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
sizeof(struct eap_tlv_request_action_tlv) +
sizeof(struct eap_tlv_pac_type_tlv));
if (tmp == NULL)
return NULL;
pos = wpabuf_put(tmp, 0);
pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
wpabuf_put(tmp, pos2 - pos);
return tmp;
}
static int eap_fast_process_decrypted(struct eap_sm *sm, static int eap_fast_process_decrypted(struct eap_sm *sm,
struct eap_fast_data *data, struct eap_fast_data *data,
struct eap_method_ret *ret, struct eap_method_ret *ret,
@ -1147,8 +1115,9 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
struct wpabuf *decrypted, struct wpabuf *decrypted,
struct wpabuf **out_data) struct wpabuf **out_data)
{ {
struct wpabuf *resp = NULL; struct wpabuf *resp = NULL, *tmp;
struct eap_fast_tlv_parse tlv; struct eap_fast_tlv_parse tlv;
int failed = 0;
if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0) if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
return 0; return 0;
@ -1168,43 +1137,84 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
req->identifier, out_data); req->identifier, out_data);
} }
if (tlv.eap_payload_tlv) { if (tlv.crypto_binding) {
resp = eap_fast_process_eap_payload_tlv( tmp = eap_fast_process_crypto_binding(sm, data, ret,
sm, data, ret, req, tlv.eap_payload_tlv, tlv.crypto_binding,
tlv.eap_payload_tlv_len); tlv.crypto_binding_len);
return eap_fast_encrypt_response(sm, data, resp, if (tmp == NULL)
req->identifier, out_data); failed = 1;
else
resp = wpabuf_concat(resp, tmp);
} }
if (tlv.crypto_binding) { if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
int final = tlv.result == EAP_TLV_RESULT_SUCCESS; tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
resp = eap_fast_process_crypto_binding(sm, data, ret, EAP_TLV_RESULT_SUCCESS, 1);
tlv.crypto_binding, resp = wpabuf_concat(resp, tmp);
tlv.crypto_binding_len, }
final);
return eap_fast_encrypt_response(sm, data, resp, if (tlv.eap_payload_tlv) {
req->identifier, out_data); tmp = eap_fast_process_eap_payload_tlv(
sm, data, ret, req, tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len);
resp = wpabuf_concat(resp, tmp);
} }
if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) { if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
"acknowledging success"); "acknowledging success");
resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); failed = 1;
return eap_fast_encrypt_response(sm, data, resp, } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
req->identifier, out_data); tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
}
if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
resp = eap_fast_process_pac(sm, data, ret, tlv.pac,
tlv.pac_len); tlv.pac_len);
return eap_fast_encrypt_response(sm, data, resp, resp = wpabuf_concat(resp, tmp);
req->identifier, out_data);
} }
if (data->current_pac == NULL && data->provisioning &&
!data->anon_provisioning) {
/*
* Need to request Tunnel PAC when using authenticated
* provisioning.
*/
wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
tmp = eap_fast_pac_request();
resp = wpabuf_concat(resp, tmp);
}
if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
resp = wpabuf_concat(resp, tmp);
} else if (failed) {
tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
resp = wpabuf_concat(resp, tmp);
}
if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
tlv.crypto_binding && data->phase2_success) {
if (data->anon_provisioning) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
"provisioning completed successfully.");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
} else {
wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
"completed successfully.");
if (data->provisioning)
ret->methodState = METHOD_MAY_CONT;
else
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
}
}
if (resp == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
"empty response packet"); "empty response packet");
return eap_fast_encrypt_response(sm, data, wpabuf_alloc(1), resp = wpabuf_alloc(1);
req->identifier, out_data); }
return eap_fast_encrypt_response(sm, data, resp, req->identifier,
out_data);
} }

View file

@ -74,6 +74,7 @@ struct eap_fast_data {
struct wpabuf *pending_phase2_resp; struct wpabuf *pending_phase2_resp;
u8 *identity; /* from PAC-Opaque */ u8 *identity; /* from PAC-Opaque */
size_t identity_len; size_t identity_len;
int eap_seq;
}; };
@ -614,26 +615,39 @@ static struct wpabuf * eap_fast_build_crypto_binding(
struct wpabuf *buf; struct wpabuf *buf;
struct eap_tlv_result_tlv *result; struct eap_tlv_result_tlv *result;
struct eap_tlv_crypto_binding_tlv *binding; struct eap_tlv_crypto_binding_tlv *binding;
int type;
buf = wpabuf_alloc(sizeof(*result) + sizeof(*binding)); buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
if (buf == NULL) if (buf == NULL)
return NULL; return NULL;
if (data->send_new_pac || data->anon_provisioning) { if (data->send_new_pac || data->anon_provisioning ||
type = EAP_TLV_INTERMEDIATE_RESULT_TLV; data->phase2_method)
data->final_result = 0; data->final_result = 0;
} else { else
type = EAP_TLV_RESULT_TLV;
data->final_result = 1; data->final_result = 1;
}
/* Result TLV */ if (!data->final_result || data->eap_seq > 1) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)"); /* Intermediate-Result */
wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
"(status=SUCCESS)");
result = wpabuf_put(buf, sizeof(*result)); result = wpabuf_put(buf, sizeof(*result));
result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | type); result->tlv_type = host_to_be16(
EAP_TLV_TYPE_MANDATORY |
EAP_TLV_INTERMEDIATE_RESULT_TLV);
result->length = host_to_be16(2); result->length = host_to_be16(2);
result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
}
if (data->final_result) {
/* Result TLV */
wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV "
"(status=SUCCESS)");
result = wpabuf_put(buf, sizeof(*result));
result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
EAP_TLV_RESULT_TLV);
result->length = host_to_be16(2);
result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
}
/* Crypto-Binding TLV */ /* Crypto-Binding TLV */
binding = wpabuf_put(buf, sizeof(*binding)); binding = wpabuf_put(buf, sizeof(*binding));
@ -828,6 +842,16 @@ static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
break; break;
case CRYPTO_BINDING: case CRYPTO_BINDING:
req = eap_fast_build_crypto_binding(sm, data); req = eap_fast_build_crypto_binding(sm, data);
if (data->phase2_method) {
/*
* Include the start of the next EAP method in the
* sequence in the same message with Crypto-Binding to
* save a round-trip.
*/
struct wpabuf *eap;
eap = eap_fast_build_phase2_req(sm, data, id);
req = wpabuf_concat(req, eap);
}
break; break;
case REQUEST_PAC: case REQUEST_PAC:
req = eap_fast_build_pac(sm, data); req = eap_fast_build_pac(sm, data);
@ -981,9 +1005,13 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
break; break;
case PHASE2_METHOD: case PHASE2_METHOD:
case CRYPTO_BINDING:
eap_fast_update_icmk(sm, data); eap_fast_update_icmk(sm, data);
eap_fast_state(data, CRYPTO_BINDING); eap_fast_state(data, CRYPTO_BINDING);
data->eap_seq++;
next_type = EAP_TYPE_NONE; next_type = EAP_TYPE_NONE;
/* TODO: could start another EAP method in sequence by setting
* next_type to the selected method */
break; break;
case FAILURE: case FAILURE:
break; break;
@ -1199,11 +1227,6 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
return; return;
} }
if (tlv.eap_payload_tlv) {
eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len);
}
if (check_crypto_binding) { if (check_crypto_binding) {
if (tlv.crypto_binding == NULL) { if (tlv.crypto_binding == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding " wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
@ -1235,7 +1258,11 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
} }
wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV " wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV "
"received - authentication completed successfully"); "received");
if (data->final_result) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
"completed successfully");
}
if (data->anon_provisioning || if (data->anon_provisioning ||
(tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
@ -1248,9 +1275,14 @@ static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered " wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
"re-keying of Tunnel PAC"); "re-keying of Tunnel PAC");
eap_fast_state(data, REQUEST_PAC); eap_fast_state(data, REQUEST_PAC);
} else } else if (data->final_result)
eap_fast_state(data, SUCCESS); eap_fast_state(data, SUCCESS);
} }
if (tlv.eap_payload_tlv) {
eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len);
}
} }

View file

@ -1,6 +1,6 @@
/* /*
* Dynamic data buffer * Dynamic data buffer
* Copyright (c) 2007, Jouni Malinen <j@w1.fi> * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -123,3 +123,40 @@ void * wpabuf_put(struct wpabuf *buf, size_t len)
} }
return tmp; return tmp;
} }
/**
* wpabuf_concat - Concatenate two buffers into a newly allocated one
* @a: First buffer
* @b: Second buffer
* Returns: wpabuf with concatenated a + b data or %NULL on failure
*
* Both buffers a and b will be freed regardless of the return value. Input
* buffers can be %NULL which is interpreted as an empty buffer.
*/
struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
{
struct wpabuf *n = NULL;
size_t len = 0;
if (b == NULL)
return a;
if (a)
len += wpabuf_len(a);
if (b)
len += wpabuf_len(b);
n = wpabuf_alloc(len);
if (n) {
if (a)
wpabuf_put_buf(n, a);
if (b)
wpabuf_put_buf(n, b);
}
wpabuf_free(a);
wpabuf_free(b);
return n;
}

View file

@ -36,6 +36,7 @@ struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
struct wpabuf * wpabuf_dup(const struct wpabuf *src); struct wpabuf * wpabuf_dup(const struct wpabuf *src);
void wpabuf_free(struct wpabuf *buf); void wpabuf_free(struct wpabuf *buf);
void * wpabuf_put(struct wpabuf *buf, size_t len); void * wpabuf_put(struct wpabuf *buf, size_t len);
struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
/** /**

View file

@ -1,5 +1,8 @@
ChangeLog for wpa_supplicant ChangeLog for wpa_supplicant
????-??-?? - v0.6.4
* added support for EAP Sequences in EAP-FAST Phase 2
2008-02-22 - v0.6.3 2008-02-22 - v0.6.3
* removed 'nai' and 'eappsk' network configuration variables that were * removed 'nai' and 'eappsk' network configuration variables that were
previously used for configuring user identity and key for EAP-PSK, previously used for configuring user identity and key for EAP-PSK,