From 950c5630768285e460aa336c78a18162fb3e0434 Mon Sep 17 00:00:00 2001 From: Stevent Li Date: Wed, 6 Feb 2013 18:52:33 +0200 Subject: [PATCH] EAP peer: Add Session-Id derivation This adds a new getSessionId() callback for EAP peer methods to allow EAP Session-Id to be derived. This commits implements this for EAP-FAST, EAP-GPSK, EAP-IKEv2, EAP-PEAP, EAP-TLS, and EAP-TTLS. Signed-hostap: Jouni Malinen --- src/eap_common/eap_gpsk_common.c | 135 +++++++++++++++++++++++++++++++ src/eap_common/eap_gpsk_common.h | 6 ++ src/eap_peer/eap.c | 33 ++++++++ src/eap_peer/eap.h | 1 + src/eap_peer/eap_fast.c | 40 +++++++++ src/eap_peer/eap_gpsk.c | 38 ++++++++- src/eap_peer/eap_i.h | 15 ++++ src/eap_peer/eap_ikev2.c | 31 +++++++ src/eap_peer/eap_peap.c | 39 +++++++++ src/eap_peer/eap_tls.c | 36 +++++++++ src/eap_peer/eap_tls_common.c | 46 +++++++++++ src/eap_peer/eap_tls_common.h | 3 + src/eap_peer/eap_ttls.c | 36 +++++++++ 13 files changed, 457 insertions(+), 2 deletions(-) diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c index 7d106dd06..7a33215f9 100644 --- a/src/eap_common/eap_gpsk_common.c +++ b/src/eap_common/eap_gpsk_common.c @@ -340,6 +340,141 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, } +static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, + u8 *kdf_out, size_t kdf_out_len, + const u8 *psk, const u8 *seed, + size_t seed_len, u8 method_type) +{ + u8 *pos, *data; + size_t data_len; + int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, + u8 *buf, size_t len); + + gkdf = NULL; + switch (csuite_specifier) { + case EAP_GPSK_CIPHER_AES: + gkdf = eap_gpsk_gkdf_cmac; + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + gkdf = eap_gpsk_gkdf_sha256; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " + "Session-Id derivation", csuite_specifier); + return -1; + } + +#define SID_LABEL "Method ID" + /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ + data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); + pos += strlen(SID_LABEL); +#undef SID_LABEL + os_memcpy(pos, &method_type, 1); + pos += 1; + WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ + pos += 4; + WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ + pos += 2; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", + data, data_len); + + if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); + + return 0; +} + + +/** + * eap_gpsk_session_id - Derive EAP-GPSK Session ID + * @psk: Pre-shared key + * @psk_len: Length of psk in bytes + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @rand_peer: 32-byte RAND_Peer + * @rand_server: 32-byte RAND_Server + * @id_peer: ID_Peer + * @id_peer_len: Length of ID_Peer + * @id_server: ID_Server + * @id_server_len: Length of ID_Server + * @method_type: EAP Authentication Method Type + * @sid: Buffer for 17-byte Session ID + * @sid_len: Buffer for returning length of Session ID + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len) +{ + u8 *seed, *pos; + u8 kdf_out[16]; + size_t seed_len; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 16, CSuite_Sel = 0x00000000 0x0001 + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; + seed = os_malloc(seed_len); + if (seed == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " + "for Session-Id derivation"); + return -1; + } + + pos = seed; + os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_peer, id_peer_len); + pos += id_peer_len; + os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_server, id_server_len); + pos += id_server_len; + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); + + ret = eap_gpsk_derive_mid_helper(specifier, + kdf_out, sizeof(kdf_out), + psk, seed, seed_len, + method_type); + + sid[0] = method_type; + os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); + *sid_len = 1 + sizeof(kdf_out); + + os_free(seed); + + return ret; +} + + /** * eap_gpsk_mic_len - Get the length of the MIC * @vendor: CSuite/Vendor diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h index e3d2b6b4a..fbcd54732 100644 --- a/src/eap_common/eap_gpsk_common.h +++ b/src/eap_common/eap_gpsk_common.h @@ -53,6 +53,12 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, const u8 *id_server, size_t id_server_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len); +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len); size_t eap_gpsk_mic_len(int vendor, int specifier); int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, int specifier, const u8 *data, size_t len, u8 *mic); diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 85c242a8d..4df88539b 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -161,6 +161,8 @@ SM_STATE(EAP, INITIALIZE) eapol_set_bool(sm, EAPOL_eapFail, FALSE); os_free(sm->eapKeyData); sm->eapKeyData = NULL; + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; sm->eapKeyAvailable = FALSE; eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with @@ -403,6 +405,13 @@ SM_STATE(EAP, METHOD) os_free(sm->eapKeyData); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); + os_free(sm->eapSessionId); + sm->eapSessionId = sm->m->getSessionId(sm, sm->eap_method_priv, + &sm->eapSessionIdLen); + if (sm->eapSessionId) { + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eapSessionId, sm->eapSessionIdLen); + } } } @@ -1462,6 +1471,8 @@ void eap_sm_abort(struct eap_sm *sm) sm->eapRespData = NULL; os_free(sm->eapKeyData); sm->eapKeyData = NULL; + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; /* This is not clearly specified in the EAP statemachines draft, but * it seems necessary to make sure that some of the EAPOL variables get @@ -2158,6 +2169,28 @@ void eap_notify_lower_layer_success(struct eap_sm *sm) } +/** + * eap_get_eapSessionId - Get Session-Id from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available + * only after a successful authentication. EAP state machine continues to manage + * the Session-Id and the caller must not change or free the returned data. + */ +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapSessionId == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapSessionIdLen; + return sm->eapSessionId; +} + + /** * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 8bccef1b1..f87f9b3b0 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -306,6 +306,7 @@ void eap_set_force_disabled(struct eap_sm *sm, int disabled); int eap_key_available(struct eap_sm *sm); void eap_notify_success(struct eap_sm *sm); void eap_notify_lower_layer_success(struct eap_sm *sm); +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len); const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 7ca5288ca..3b8d803dc 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -53,6 +53,8 @@ struct eap_fast_data { int session_ticket_used; u8 key_data[EAP_FAST_KEY_LEN]; + u8 *session_id; + size_t id_len; u8 emsk[EAP_EMSK_LEN]; int success; @@ -238,6 +240,7 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) pac = pac->next; eap_fast_free_pac(prev); } + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -785,6 +788,21 @@ static struct wpabuf * eap_fast_process_crypto_binding( return NULL; } + if (!data->anon_provisioning && data->phase2_success) { + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id( + sm, &data->ssl, EAP_TYPE_FAST, &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " + "Session-Id"); + wpabuf_free(resp); + return NULL; + } + } + pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) pos, _bind, cmk); @@ -1604,6 +1622,8 @@ static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) os_free(data); return NULL; } + os_free(data->session_id); + data->session_id = NULL; if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); @@ -1662,6 +1682,25 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *id; + + if (!data->success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; @@ -1696,6 +1735,7 @@ int eap_peer_fast_register(void) eap->process = eap_fast_process; eap->isKeyAvailable = eap_fast_isKeyAvailable; eap->getKey = eap_fast_getKey; + eap->getSessionId = eap_fast_get_session_id; eap->get_status = eap_fast_get_status; #if 0 eap->has_reauth_data = eap_fast_has_reauth_data; diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 2bd0d480f..8a0644d02 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -23,8 +23,8 @@ struct eap_gpsk_data { size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; - u8 session_id; - int session_id_set; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; u8 *id_server; @@ -354,6 +354,21 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, return NULL; } + if (eap_gpsk_derive_session_id(data->psk, data->psk_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + data->id_server, data->id_server_len, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + /* No PD_Payload_1 */ wpabuf_put_be16(resp, 0); @@ -708,6 +723,24 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_peer_gpsk_register(void) { struct eap_method *eap; @@ -724,6 +757,7 @@ int eap_peer_gpsk_register(void) eap->isKeyAvailable = eap_gpsk_isKeyAvailable; eap->getKey = eap_gpsk_getKey; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index dd943174e..62c867ca2 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -261,6 +261,19 @@ struct eap_method { * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; @@ -298,6 +311,8 @@ struct eap_sm { Boolean eapKeyAvailable; /* peer to lower layer */ u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ + u8 *eapSessionId; /* peer to lower layer */ + size_t eapSessionIdLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ /* not defined in RFC 4137 */ Boolean changed; diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index a227f8b14..09a655ecf 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -475,6 +475,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_peer_ikev2_register(void) { struct eap_method *eap; @@ -492,6 +522,7 @@ int eap_peer_ikev2_register(void) eap->isKeyAvailable = eap_ikev2_isKeyAvailable; eap->getKey = eap_ikev2_getKey; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 7fff1458a..3b9320907 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -56,6 +56,8 @@ struct eap_peap_data { int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; @@ -179,6 +181,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -1107,6 +1110,20 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "derive key"); } + os_free(data->session_id); + data->session_id = + eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_PEAP, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, + "EAP-PEAP: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " + "derive Session-Id"); + } + if (sm->workaround && data->resuming) { /* * At least few RADIUS servers (Aegis v1.1.6; @@ -1178,6 +1195,8 @@ static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) struct eap_peap_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1260,6 +1279,25 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_peap_register(void) { struct eap_method *eap; @@ -1279,6 +1317,7 @@ int eap_peer_peap_register(void) eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; eap->init_for_reauth = eap_peap_init_for_reauth; + eap->getSessionId = eap_peap_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 061a72b10..d2066cd85 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -21,6 +21,8 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; + u8 *session_id; + size_t id_len; void *ssl_ctx; u8 eap_type; }; @@ -103,6 +105,7 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv) return; eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); os_free(data); } @@ -165,6 +168,17 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, } else { wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); } + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id"); + } } @@ -228,6 +242,8 @@ static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) struct eap_tls_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -289,6 +305,25 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *id; + + if (data->session_id == NULL) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_tls_register(void) { struct eap_method *eap; @@ -304,6 +339,7 @@ int eap_peer_tls_register(void) eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; + eap->getSessionId = eap_tls_get_session_id; eap->get_status = eap_tls_get_status; eap->has_reauth_data = eap_tls_has_reauth_data; eap->deinit_for_reauth = eap_tls_deinit_for_reauth; diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index aedd85a79..a777bb013 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -339,6 +339,52 @@ fail: } +/** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + /* + * TLS library did not support session ID generation, + * so get the needed TLS session parameters + */ + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + /** * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 91d3a25a0..1a5e0f89e 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -94,6 +94,9 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const u8 *in_data, size_t in_len, diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 9360a424c..5091bf084 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -54,6 +54,8 @@ struct eap_ttls_data { int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; @@ -140,6 +142,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) os_free(data->phase2_eap_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -222,6 +225,17 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TTLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); + } + return 0; } @@ -1528,6 +1542,8 @@ static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) struct eap_ttls_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1612,6 +1628,25 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_ttls_register(void) { struct eap_method *eap; @@ -1627,6 +1662,7 @@ int eap_peer_ttls_register(void) eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; + eap->getSessionId = eap_ttls_get_session_id; eap->get_status = eap_ttls_get_status; eap->has_reauth_data = eap_ttls_has_reauth_data; eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;