#include "includes.h"
#include "common.h"
-#include "aes_wrap.h"
-#include "sha1.h"
-#include "eap_i.h"
-#include "eap_tls_common.h"
-#include "tls.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_fast_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
static void eap_fast_reset(struct eap_sm *sm, void *priv);
#define PAC_OPAQUE_TYPE_LIFETIME 2
#define PAC_OPAQUE_TYPE_IDENTITY 3
-/* PAC-Key lifetime in seconds (hard limit) */
-#define PAC_KEY_LIFETIME (7 * 24 * 60 * 60)
-
-/*
- * PAC-Key refresh time in seconds (soft limit on remaining hard limit). The
- * server will generate a new PAC-Key when this number of seconds (or fewer)
- * of the lifetime.
- */
-#define PAC_KEY_REFRESH_TIME (1 * 24 * 60 * 60)
-
-
struct eap_fast_data {
struct eap_ssl_data ssl;
enum {
struct eap_fast_key_block_provisioning *key_block_p;
u8 simck[EAP_FAST_SIMCK_LEN];
- u8 cmk[20];
+ u8 cmk[EAP_FAST_CMK_LEN];
int simck_idx;
u8 pac_opaque_encr[16];
- char *srv_id;
+ u8 *srv_id;
+ size_t srv_id_len;
+ char *srv_id_info;
int anon_provisioning;
int send_new_pac; /* server triggered re-keying of Tunnel PAC */
struct wpabuf *pending_phase2_resp;
u8 *identity; /* from PAC-Opaque */
size_t identity_len;
+ int eap_seq;
+ int tnc_started;
+
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
};
if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore "
"(lifetime=%ld now=%ld)", lifetime, now.sec);
- os_free(buf);
- return 0;
- }
-
- if (lifetime - now.sec < PAC_KEY_REFRESH_TIME)
+ data->send_new_pac = 2;
+ /*
+ * Allow PAC to be used to allow a PAC update with some level
+ * of server authentication (i.e., do not fall back to full TLS
+ * handshake since we cannot be sure that the peer would be
+ * able to validate server certificate now). However, reject
+ * the authentication since the PAC was not valid anymore. Peer
+ * can connect again with the newly provisioned PAC after this.
+ */
+ } else if (lifetime - now.sec < data->pac_key_refresh_time) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send "
+ "an update if authentication succeeds");
data->send_new_pac = 1;
+ }
eap_fast_derive_master_secret(pac_key, server_random, client_random,
master_secret);
if (key_len > isk_len)
key_len = isk_len;
- os_memcpy(isk, key, key_len);
+ if (key_len == 32 &&
+ data->phase2_method->vendor == EAP_VENDOR_IETF &&
+ data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+ /*
+ * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+ * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+ * ISK for EAP-FAST cryptobinding.
+ */
+ os_memcpy(isk, key + 16, 16);
+ os_memcpy(isk + 16, key, 16);
+ } else
+ os_memcpy(isk, key, key_len);
os_free(key);
return 0;
os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
data->simck, EAP_FAST_SIMCK_LEN);
- os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, 20);
- wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", data->cmk, 20);
+ os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+ data->cmk, EAP_FAST_CMK_LEN);
return 0;
}
eap_fast_reset(sm, data);
return NULL;
}
- data->srv_id = os_strdup(sm->eap_fast_a_id);
+ data->srv_id = os_malloc(sm->eap_fast_a_id_len);
if (data->srv_id == NULL) {
eap_fast_reset(sm, data);
return NULL;
}
+ os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+ data->srv_id_len = sm->eap_fast_a_id_len;
+
+ if (sm->eap_fast_a_id_info == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured");
+ eap_fast_reset(sm, data);
+ return NULL;
+ }
+ data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+ if (data->srv_id_info == NULL) {
+ eap_fast_reset(sm, data);
+ return NULL;
+ }
+
+ /* PAC-Key lifetime in seconds (hard limit) */
+ data->pac_key_lifetime = sm->pac_key_lifetime;
+
+ /*
+ * PAC-Key refresh time in seconds (soft limit on remaining hard
+ * limit). The server will generate a new PAC-Key when this number of
+ * seconds (or fewer) of the lifetime remains.
+ */
+ data->pac_key_refresh_time = sm->pac_key_refresh_time;
return data;
}
data->phase2_method->reset(sm, data->phase2_priv);
eap_server_tls_ssl_deinit(sm, &data->ssl);
os_free(data->srv_id);
+ os_free(data->srv_id_info);
os_free(data->key_block_p);
wpabuf_free(data->pending_phase2_resp);
os_free(data->identity);
struct eap_fast_data *data, u8 id)
{
struct wpabuf *req;
- size_t srv_id_len = os_strlen(data->srv_id);
req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST,
- 1 + sizeof(struct pac_tlv_hdr) + srv_id_len,
+ 1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len,
EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for"
wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version);
/* RFC 4851, 4.1.1. Authority ID Data */
- eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, srv_id_len);
+ eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
eap_fast_state(data, PHASE1);
}
-static struct wpabuf * eap_fast_build_req(struct eap_sm *sm,
- struct eap_fast_data *data, u8 id)
-{
- int res;
- struct wpabuf *req;
-
- res = eap_server_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_FAST,
- data->fast_version, id, &req);
-
- if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
- if (eap_fast_phase1_done(sm, data) < 0) {
- os_free(req);
- return NULL;
- }
- }
-
- if (res == 1)
- return eap_server_tls_build_ack(id, EAP_TYPE_FAST,
- data->fast_version);
- return req;
-}
-
-
-static struct wpabuf * eap_fast_encrypt(struct eap_sm *sm,
- struct eap_fast_data *data,
- u8 id, u8 *plain, size_t plain_len)
-{
- int res;
- struct wpabuf *buf;
-
- /* TODO: add support for fragmentation, if needed. This will need to
- * add TLS Message Length field, if the frame is fragmented. */
- buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST,
- 1 + data->ssl.tls_out_limit,
- EAP_CODE_REQUEST, id);
- if (buf == NULL)
- return NULL;
-
- wpabuf_put_u8(buf, data->fast_version);
-
- res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
- plain, plain_len, wpabuf_put(buf, 0),
- data->ssl.tls_out_limit);
- if (res < 0) {
- wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 "
- "data");
- wpabuf_free(buf);
- return NULL;
- }
-
- wpabuf_put(buf, res);
- eap_update_len(buf);
-
- return buf;
-}
-
-
static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm,
struct eap_fast_data *data,
u8 id)
{
struct wpabuf *buf;
struct eap_tlv_result_tlv *result;
- struct eap_tlv_crypto_binding__tlv *binding;
- int type;
+ struct eap_tlv_crypto_binding_tlv *binding;
- buf = wpabuf_alloc(sizeof(*result) + sizeof(*binding));
+ buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
if (buf == NULL)
return NULL;
- if (data->send_new_pac || data->anon_provisioning) {
- type = EAP_TLV_INTERMEDIATE_RESULT_TLV;
+ if (data->send_new_pac || data->anon_provisioning ||
+ data->phase2_method)
data->final_result = 0;
- } else {
- type = EAP_TLV_RESULT_TLV;
+ else
data->final_result = 1;
+
+ if (!data->final_result || data->eap_seq > 1) {
+ /* Intermediate-Result */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
+ "(status=SUCCESS)");
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(
+ EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_INTERMEDIATE_RESULT_TLV);
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
}
- /* 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 | type);
- result->length = host_to_be16(2);
- 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 */
binding = wpabuf_put(buf, sizeof(*binding));
* Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV )
*/
- hmac_sha1(data->cmk, 20, (u8 *) binding, sizeof(*binding),
+ hmac_sha1(data->cmk, EAP_FAST_CMK_LEN,
+ (u8 *) binding, sizeof(*binding),
binding->compound_mac);
wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d "
u8 *pac_buf, *pac_opaque;
struct wpabuf *buf;
u8 *pos;
- size_t buf_len, srv_id_len, pac_len;
+ size_t buf_len, srv_id_info_len, pac_len;
struct eap_tlv_hdr *pac_tlv;
struct pac_tlv_hdr *pac_info;
struct eap_tlv_result_tlv *result;
if (pac_buf == NULL)
return NULL;
- srv_id_len = os_strlen(data->srv_id);
+ srv_id_info_len = os_strlen(data->srv_id_info);
pos = pac_buf;
*pos++ = PAC_OPAQUE_TYPE_KEY;
*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
*pos++ = 4;
- WPA_PUT_BE32(pos, now.sec + PAC_KEY_LIFETIME);
+ WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
pos += 4;
if (sm->identity) {
}
pac_len = pos - pac_buf;
- if (pac_len % 8) {
+ while (pac_len % 8) {
*pos++ = PAC_OPAQUE_TYPE_PAD;
pac_len++;
}
buf_len = sizeof(*pac_tlv) +
sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN +
sizeof(struct pac_tlv_hdr) + pac_len +
- 2 * srv_id_len + 100 + sizeof(*result);
+ data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
buf = wpabuf_alloc(buf_len);
if (buf == NULL) {
os_free(pac_opaque);
return NULL;
}
+ /* Result TLV */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
+ result = wpabuf_put(buf, sizeof(*result));
+ WPA_PUT_BE16((u8 *) &result->tlv_type,
+ EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV);
+ WPA_PUT_BE16((u8 *) &result->length, 2);
+ WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS);
+
/* PAC TLV */
wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV");
pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
/* PAC-Lifetime (inside PAC-Info) */
eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
- wpabuf_put_be32(buf, now.sec + PAC_KEY_LIFETIME);
+ wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
/* A-ID (inside PAC-Info) */
- eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, srv_id_len);
+ eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
/* Note: headers may be misaligned after A-ID */
/* A-ID-Info (inside PAC-Info) */
- eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id, srv_id_len);
+ eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
+ srv_id_info_len);
/* PAC-Type (inside PAC-Info) */
eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
- /* Result TLV */
- wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
- result = wpabuf_put(buf, sizeof(*result));
- WPA_PUT_BE16((u8 *) &result->tlv_type,
- EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV);
- WPA_PUT_BE16((u8 *) &result->length, 2);
- WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS);
-
return buf;
}
static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_fast_data *data = priv;
- struct wpabuf *req;
+ struct wpabuf *req = NULL;
struct wpabuf *encr;
- if (data->state == START)
- return eap_fast_build_start(sm, data, id);
+ if (data->ssl.state == FRAG_ACK) {
+ return eap_server_tls_build_ack(id, EAP_TYPE_FAST,
+ data->fast_version);
+ }
- if (data->state == PHASE1)
- return eap_fast_build_req(sm, data, id);
+ if (data->ssl.state == WAIT_FRAG_ACK) {
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+ data->fast_version, id);
+ }
switch (data->state) {
+ case START:
+ return eap_fast_build_start(sm, data, id);
+ case PHASE1:
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ if (eap_fast_phase1_done(sm, data) < 0)
+ return NULL;
+ }
+ break;
case PHASE2_ID:
case PHASE2_METHOD:
req = eap_fast_build_phase2_req(sm, data, id);
break;
case CRYPTO_BINDING:
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);
+ eap_fast_state(data, PHASE2_METHOD);
+ }
break;
case REQUEST_PAC:
req = eap_fast_build_pac(sm, data);
return NULL;
}
- if (req == NULL)
- return NULL;
-
- wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs",
- req);
- encr = eap_fast_encrypt(sm, data, id, wpabuf_mhead(req),
- wpabuf_len(req));
- wpabuf_free(req);
+ if (req) {
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 "
+ "TLVs", req);
+ encr = eap_server_tls_encrypt(sm, &data->ssl,
+ wpabuf_mhead(req),
+ wpabuf_len(req));
+ wpabuf_free(req);
+
+ wpabuf_free(data->ssl.out_buf);
+ data->ssl.out_used = 0;
+ data->ssl.out_buf = encr;
+ }
- return encr;
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+ data->fast_version, id);
}
left = in_len - sizeof(*hdr);
wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; "
"allowed types", pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+ if (m && m->vendor == EAP_VENDOR_IETF &&
+ m->method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required "
+ "TNC negotiation");
+ next_type = eap_fast_req_failure(sm, data);
+ eap_fast_phase2_init(sm, data, next_type);
+ return;
+ }
+#endif /* EAP_SERVER_TNC */
eap_sm_process_nak(sm, pos + 1, left - 1);
if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
sm->user->methods[sm->user_eap_method_index].method !=
wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
break;
case PHASE2_METHOD:
+ case CRYPTO_BINDING:
eap_fast_update_icmk(sm, data);
eap_fast_state(data, CRYPTO_BINDING);
+ data->eap_seq++;
next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+ if (sm->tnc && !data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC");
+ next_type = EAP_TYPE_TNC;
+ data->tnc_started = 1;
+ }
+#endif /* EAP_SERVER_TNC */
break;
case FAILURE:
break;
hdr = (struct eap_hdr *) in_data;
if (in_len < (int) sizeof(*hdr)) {
wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
- "EAP frame (len=%d)", in_len);
+ "EAP frame (len=%lu)", (unsigned long) in_len);
eap_fast_req_failure(sm, data);
return;
}
len = be_to_host16(hdr->length);
if (len > in_len) {
wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in "
- "Phase 2 EAP frame (len=%d hdr->length=%d)",
- in_len, len);
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) in_len, (unsigned long) len);
eap_fast_req_failure(sm, data);
return;
}
wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d "
- "identifier=%d length=%d", hdr->code, hdr->identifier, len);
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
switch (hdr->code) {
case EAP_CODE_RESPONSE:
eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len);
static int eap_fast_validate_crypto_binding(
- struct eap_fast_data *data, struct eap_tlv_crypto_binding__tlv *b,
+ struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b,
size_t bind_len)
{
- u8 cmac[20];
+ u8 cmac[SHA1_MAC_LEN];
wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: "
"Version %d Received Version %d SubType %d",
wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for "
"Compound MAC calculation",
(u8 *) b, bind_len);
- hmac_sha1(data->cmk, 20, (u8 *) b, bind_len, b->compound_mac);
+ hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
+ b->compound_mac);
if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) {
wpa_hexdump(MSG_MSGDUMP,
"EAP-FAST: Calculated Compound MAC",
wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received "
"- PAC provisioning succeeded");
- eap_fast_state(data, data->anon_provisioning ?
+ eap_fast_state(data, (data->anon_provisioning ||
+ data->send_new_pac == 2) ?
FAILURE : SUCCESS);
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 (tlv.crypto_binding == NULL) {
wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
}
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 &&
+ sm->eap_fast_prov != ANON_PROV &&
+ sm->eap_fast_prov != BOTH_PROV) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+ "use unauthenticated provisioning which is "
+ "disabled");
+ eap_fast_state(data, FAILURE);
+ return;
+ }
+
+ if (sm->eap_fast_prov != AUTH_PROV &&
+ sm->eap_fast_prov != BOTH_PROV &&
+ tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
+ eap_fast_pac_type(tlv.pac, tlv.pac_len,
+ PAC_TYPE_TUNNEL_PAC)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+ "use authenticated provisioning which is "
+ "disabled");
+ eap_fast_state(data, FAILURE);
+ return;
+ }
if (data->anon_provisioning ||
(tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
"re-keying of Tunnel PAC");
eap_fast_state(data, REQUEST_PAC);
- } else
+ } else if (data->final_result)
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);
+ }
}
static void eap_fast_process_phase2(struct eap_sm *sm,
struct eap_fast_data *data,
- const u8 *in_data, size_t in_len)
+ struct wpabuf *in_buf)
{
u8 *in_decrypted;
- int len_decrypted, res;
+ int len_decrypted;
size_t buf_len;
+ u8 *in_data;
+ size_t in_len;
+
+ in_data = wpabuf_mhead(in_buf);
+ in_len = wpabuf_len(in_buf);
wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
" Phase 2", (unsigned long) in_len);
return;
}
- /* FIX: get rid of const -> non-const typecast */
- res = eap_server_tls_data_reassemble(sm, &data->ssl, (u8 **) &in_data,
- &in_len);
- if (res < 0 || res == 1)
- return;
-
buf_len = in_len;
- if (data->ssl.tls_in_total > buf_len)
- buf_len = data->ssl.tls_in_total;
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf_len += 500;
+ buf_len *= 3;
in_decrypted = os_malloc(buf_len);
if (in_decrypted == NULL) {
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory "
"for decryption");
return;
len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
in_data, in_len,
in_decrypted, buf_len);
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
if (len_decrypted < 0) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
"data");
}
-static void eap_fast_process(struct eap_sm *sm, void *priv,
- struct wpabuf *respData)
+static int eap_fast_process_version(struct eap_sm *sm, void *priv,
+ int peer_version)
{
struct eap_fast_data *data = priv;
- const u8 *pos;
- u8 flags;
- size_t left;
- unsigned int tls_msg_len;
- int peer_version;
- u8 next_type;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData,
- &left);
- if (pos == NULL || left < 1)
- return;
- flags = *pos++;
- left--;
- wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - "
- "Flags 0x%02x", (unsigned long) wpabuf_len(respData),
- flags);
- data->peer_version = peer_version = flags & EAP_PEAP_VERSION_MASK;
+ data->peer_version = peer_version;
+
if (data->force_version >= 0 && peer_version != data->force_version) {
wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced"
" version (forced=%d peer=%d) - reject",
data->force_version, peer_version);
- eap_fast_state(data, FAILURE);
- return;
+ return -1;
}
+
if (peer_version < data->fast_version) {
wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; "
"use version %d",
peer_version, data->fast_version, peer_version);
data->fast_version = peer_version;
}
- if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
- if (left < 4) {
- wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS "
- "length");
- eap_fast_state(data, FAILURE);
- return;
- }
- tls_msg_len = WPA_GET_BE32(pos);
- wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d",
- tls_msg_len);
- if (data->ssl.tls_in_left == 0) {
- data->ssl.tls_in_total = tls_msg_len;
- data->ssl.tls_in_left = tls_msg_len;
- os_free(data->ssl.tls_in);
- data->ssl.tls_in = NULL;
- data->ssl.tls_in_len = 0;
- }
- pos += 4;
- left -= 4;
+
+ return 0;
+}
+
+
+static int eap_fast_process_phase1(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed");
+ eap_fast_state(data, FAILURE);
+ return -1;
}
- switch (data->state) {
- case PHASE1:
- if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) <
- 0) {
- wpa_printf(MSG_INFO, "EAP-FAST: TLS processing "
- "failed");
- eap_fast_state(data, FAILURE);
- }
+ if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+ wpabuf_len(data->ssl.out_buf) > 0)
+ return 1;
- if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
- data->ssl.tls_out_len > 0)
- break;
+ /*
+ * Phase 1 was completed with the received message (e.g., when using
+ * abbreviated handshake), so Phase 2 can be started immediately
+ * without having to send through an empty message to the peer.
+ */
- /*
- * Phase 1 was completed with the received message (e.g., when
- * using abbreviated handshake), so Phase 2 can be started
- * immediately without having to send through an empty message
- * to the peer.
- */
+ return eap_fast_phase1_done(sm, data);
+}
- if (eap_fast_phase1_done(sm, data) < 0)
- break;
- /* fall through to PHASE2_START */
- case PHASE2_START:
- if (data->identity) {
- os_free(sm->identity);
- sm->identity = data->identity;
- data->identity = NULL;
- sm->identity_len = data->identity_len;
- data->identity_len = 0;
- if (eap_user_get(sm, sm->identity, sm->identity_len, 1)
- != 0) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: "
- "Phase2 Identity not found "
- "in the user database",
- sm->identity,
- sm->identity_len);
- next_type = eap_fast_req_failure(sm, data);
- } else {
- wpa_printf(MSG_DEBUG, "EAP-FAST: Identity "
- "already known - skip Phase 2 "
- "Identity Request");
- next_type = sm->user->methods[0].method;
- sm->user_eap_method_index = 1;
- }
+static void eap_fast_process_phase2_start(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 next_type;
- eap_fast_state(data, PHASE2_METHOD);
+ if (data->identity) {
+ os_free(sm->identity);
+ sm->identity = data->identity;
+ data->identity = NULL;
+ sm->identity_len = data->identity_len;
+ data->identity_len = 0;
+ sm->require_identity_match = 1;
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: "
+ "Phase2 Identity not found "
+ "in the user database",
+ sm->identity, sm->identity_len);
+ next_type = eap_fast_req_failure(sm, data);
} else {
- eap_fast_state(data, PHASE2_ID);
- next_type = EAP_TYPE_IDENTITY;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already "
+ "known - skip Phase 2 Identity Request");
+ next_type = sm->user->methods[0].method;
+ sm->user_eap_method_index = 1;
}
- eap_fast_phase2_init(sm, data, next_type);
+ eap_fast_state(data, PHASE2_METHOD);
+ } else {
+ eap_fast_state(data, PHASE2_ID);
+ next_type = EAP_TYPE_IDENTITY;
+ }
+
+ eap_fast_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_fast_data *data = priv;
+
+ switch (data->state) {
+ case PHASE1:
+ if (eap_fast_process_phase1(sm, data))
+ break;
+
+ /* fall through to PHASE2_START */
+ case PHASE2_START:
+ eap_fast_process_phase2_start(sm, data);
break;
case PHASE2_ID:
case PHASE2_METHOD:
case CRYPTO_BINDING:
case REQUEST_PAC:
- eap_fast_process_phase2(sm, data, pos, left);
+ eap_fast_process_phase2(sm, data, data->ssl.in_buf);
break;
default:
wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s",
data->state, __func__);
break;
}
+}
- if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) {
- wpa_printf(MSG_INFO, "EAP-FAST: Locally detected fatal error "
- "in TLS processing");
+
+static void eap_fast_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_fast_data *data = priv;
+ if (eap_server_tls_process(sm, &data->ssl, respData, data,
+ EAP_TYPE_FAST, eap_fast_process_version,
+ eap_fast_process_msg) < 0)
eap_fast_state(data, FAILURE);
- }
}