EAP-FAST: Add peer identity into EAP-FAST PAC-Opaque

This allows Phase 2 Identity Request to be skipped if the identity is
already known from PAC-Opaque received in TLS handshake in order to save
one roundtrip from normal authentication.
This commit is contained in:
Jouni Malinen 2008-02-27 17:55:40 -08:00
parent 67b941993a
commit 829f14be17
2 changed files with 111 additions and 22 deletions

View File

@ -1,5 +1,9 @@
ChangeLog for hostapd
????-??-?? - v0.6.4
* added peer identity into EAP-FAST PAC-Opaque and skip Phase 2
Identity Request if identity is already known
2008-02-22 - v0.6.3
* fixed Reassociation Response callback processing when using internal
MLME (driver_{hostap,nl80211,test}.c)

View File

@ -31,6 +31,7 @@ static void eap_fast_reset(struct eap_sm *sm, void *priv);
#define PAC_OPAQUE_TYPE_PAD 0
#define PAC_OPAQUE_TYPE_KEY 1
#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)
@ -71,6 +72,8 @@ struct eap_fast_data {
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;
};
@ -133,6 +136,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
u8 *buf, *pos, *end, *pac_key = NULL;
os_time_t lifetime = 0;
struct os_time now;
u8 *identity = NULL;
size_t identity_len = 0;
wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)",
@ -217,6 +222,10 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
}
lifetime = WPA_GET_BE32(pos + 2);
break;
case PAC_OPAQUE_TYPE_IDENTITY:
identity = pos + 2;
identity_len = pos[1];
break;
}
pos += 2 + pos[1];
@ -229,6 +238,17 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
return -1;
}
if (identity) {
wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from "
"PAC-Opaque", identity, identity_len);
os_free(data->identity);
data->identity = os_malloc(identity_len);
if (data->identity) {
os_memcpy(data->identity, identity, identity_len);
data->identity_len = identity_len;
}
}
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);
@ -517,6 +537,7 @@ static void eap_fast_reset(struct eap_sm *sm, void *priv)
os_free(data->srv_id);
os_free(data->key_block_p);
wpabuf_free(data->pending_phase2_resp);
os_free(data->identity);
os_free(data);
}
@ -756,45 +777,80 @@ static struct wpabuf * eap_fast_build_crypto_binding(
static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
struct eap_fast_data *data)
{
u8 pac_key[2 + EAP_FAST_PAC_KEY_LEN + 6];
u8 pac_opaque[8 + EAP_FAST_PAC_KEY_LEN + 8];
u8 pac_key[EAP_FAST_PAC_KEY_LEN];
u8 *pac_buf, *pac_opaque;
struct wpabuf *buf;
u8 *pos;
size_t buf_len, srv_id_len;
size_t buf_len, srv_id_len, pac_len;
struct eap_tlv_hdr *pac_tlv;
struct pac_tlv_hdr *hdr, *pac_info;
struct eap_tlv_result_tlv *result;
struct os_time now;
srv_id_len = os_strlen(data->srv_id);
pac_key[0] = PAC_OPAQUE_TYPE_KEY;
pac_key[1] = EAP_FAST_PAC_KEY_LEN;
if (os_get_random(pac_key + 2, EAP_FAST_PAC_KEY_LEN) < 0)
return NULL;
if (os_get_time(&now) < 0)
if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
os_get_time(&now) < 0)
return NULL;
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
pac_key + 2, EAP_FAST_PAC_KEY_LEN);
pos = pac_key + 2 + EAP_FAST_PAC_KEY_LEN;
pac_key, EAP_FAST_PAC_KEY_LEN);
pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) +
(2 + sm->identity_len) + 8;
pac_buf = os_malloc(pac_len);
if (pac_buf == NULL)
return NULL;
srv_id_len = os_strlen(data->srv_id);
pos = pac_buf;
*pos++ = PAC_OPAQUE_TYPE_KEY;
*pos++ = EAP_FAST_PAC_KEY_LEN;
os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN);
pos += EAP_FAST_PAC_KEY_LEN;
*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
*pos++ = 4;
WPA_PUT_BE32(pos, now.sec + PAC_KEY_LIFETIME);
pos += 4;
if (aes_wrap(data->pac_opaque_encr, sizeof(pac_key) / 8, pac_key,
pac_opaque) < 0)
if (sm->identity) {
*pos++ = PAC_OPAQUE_TYPE_IDENTITY;
*pos++ = sm->identity_len;
os_memcpy(pos, sm->identity, sm->identity_len);
pos += sm->identity_len;
}
pac_len = pos - pac_buf;
if (pac_len % 8) {
*pos++ = PAC_OPAQUE_TYPE_PAD;
pac_len++;
}
pac_opaque = os_malloc(pac_len + 8);
if (pac_opaque == NULL) {
os_free(pac_buf);
return NULL;
}
if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf,
pac_opaque) < 0) {
os_free(pac_buf);
os_free(pac_opaque);
return NULL;
}
os_free(pac_buf);
pac_len += 8;
wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
pac_opaque, sizeof(pac_opaque));
pac_opaque, pac_len);
buf_len = sizeof(*pac_tlv) +
sizeof(*hdr) + EAP_FAST_PAC_KEY_LEN +
sizeof(*hdr) + sizeof(pac_opaque) +
sizeof(*hdr) + pac_len +
2 * srv_id_len + 100 + sizeof(*result);
buf = wpabuf_alloc(buf_len);
if (buf == NULL)
if (buf == NULL) {
os_free(pac_opaque);
return NULL;
}
/* PAC TLV */
wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV");
@ -806,13 +862,14 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
hdr = wpabuf_put(buf, sizeof(*hdr));
hdr->type = host_to_be16(PAC_TYPE_PAC_KEY);
hdr->len = host_to_be16(EAP_FAST_PAC_KEY_LEN);
wpabuf_put_data(buf, pac_key + 2, EAP_FAST_PAC_KEY_LEN);
wpabuf_put_data(buf, pac_key, EAP_FAST_PAC_KEY_LEN);
/* PAC-Opaque */
hdr = wpabuf_put(buf, sizeof(*hdr));
hdr->type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
hdr->len = host_to_be16(sizeof(pac_opaque));
wpabuf_put_data(buf, pac_opaque, sizeof(pac_opaque));
hdr->len = host_to_be16(pac_len);
wpabuf_put_data(buf, pac_opaque, pac_len);
os_free(pac_opaque);
/* PAC-Info */
pac_info = wpabuf_put(buf, sizeof(*pac_info));
@ -1524,6 +1581,7 @@ static void eap_fast_process(struct eap_sm *sm, void *priv,
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);
@ -1594,8 +1652,35 @@ static void eap_fast_process(struct eap_sm *sm, void *priv,
/* fall through to PHASE2_START */
case PHASE2_START:
eap_fast_state(data, PHASE2_ID);
eap_fast_phase2_init(sm, data, EAP_TYPE_IDENTITY);
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;
}
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);
break;
case PHASE2_ID:
case PHASE2_METHOD: