#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);
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) {
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);
/* 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);
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;
}