#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 {
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 */
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;
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)
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);
return NULL;
}
- if (req == NULL)
- return NULL;
+ 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);
- 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);
+ 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_TNC
+#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 "
eap_fast_phase2_init(sm, data, next_type);
return;
}
-#endif /* EAP_TNC */
+#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 !=
eap_fast_state(data, CRYPTO_BINDING);
data->eap_seq++;
next_type = EAP_TYPE_NONE;
-#ifdef EAP_TNC
+#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_TNC */
+#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);
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;
}
"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 &&
eap_fast_pac_type(tlv.pac, tlv.pac_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
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 int eap_fast_process_version(struct eap_fast_data *data,
+static int eap_fast_process_version(struct eap_sm *sm, void *priv,
int peer_version)
{
+ struct eap_fast_data *data = priv;
+
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 -1;
}
}
-static int eap_fast_process_length(struct eap_fast_data *data,
- const u8 **pos, size_t *left)
-{
- u32 tls_msg_len;
-
- if (*left < 4) {
- wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS "
- "length");
- eap_fast_state(data, FAILURE);
- return -1;
- }
-
- 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,
- const u8 *pos, size_t left)
+ struct eap_fast_data *data)
{
- if (eap_server_tls_process_helper(sm, &data->ssl, pos, left) < 0) {
+ 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;
}
if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
- data->ssl.tls_out_len > 0)
+ wpabuf_len(data->ssl.out_buf) > 0)
return 1;
/*
}
-static void eap_fast_process(struct eap_sm *sm, void *priv,
- struct wpabuf *respData)
+static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
{
struct eap_fast_data *data = priv;
- const u8 *pos;
- u8 flags;
- size_t left;
-
- 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);
-
- if (eap_fast_process_version(data, flags & EAP_PEAP_VERSION_MASK))
- return;
-
- if ((flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) &&
- eap_fast_process_length(data, &pos, &left))
- return;
switch (data->state) {
case PHASE1:
- if (eap_fast_process_phase1(sm, data, pos, left))
+ if (eap_fast_process_phase1(sm, data))
break;
/* fall through to PHASE2_START */
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);
- }
}