diff options
Diffstat (limited to 'contrib/hostapd/eap_ttls.c')
-rw-r--r-- | contrib/hostapd/eap_ttls.c | 495 |
1 files changed, 402 insertions, 93 deletions
diff --git a/contrib/hostapd/eap_ttls.c b/contrib/hostapd/eap_ttls.c index 569b1c3..1c0f17e 100644 --- a/contrib/hostapd/eap_ttls.c +++ b/contrib/hostapd/eap_ttls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt) - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -23,11 +20,20 @@ #include "eap_tls_common.h" #include "ms_funcs.h" #include "md5.h" +#include "sha1.h" #include "crypto.h" #include "tls.h" #include "eap_ttls.h" -#define EAP_TTLS_VERSION 0 + +/* Maximum supported PEAP version + * 0 = draft-ietf-pppext-eap-ttls-03.txt / draft-funk-eap-ttls-v0-00.txt + * 1 = draft-funk-eap-ttls-v1-00.txt + */ +#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ + + +#define MSCHAPV2_KEY_LEN 16 static void eap_ttls_reset(struct eap_sm *sm, void *priv); @@ -37,15 +43,17 @@ struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE + PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE } state; int ttls_version; + int force_version; const struct eap_method *phase2_method; void *phase2_priv; int mschapv2_resp_ok; u8 mschapv2_auth_response[20]; u8 mschapv2_ident; + int tls_ia_configured; }; @@ -62,6 +70,8 @@ static const char * eap_ttls_state_txt(int state) return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; + case PHASE_FINISHED: + return "PHASE_FINISHED"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -173,11 +183,16 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " "length=%d", (int) avp_code, avp_flags, (int) avp_length); - if (avp_length > left) { + if ((int) avp_length > left) { wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " "(len=%d, left=%d) - dropped", (int) avp_length, left); - return -1; + goto fail; + } + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " + "%d", avp_length); + goto fail; } dpos = (u8 *) (avp + 1); dlen = avp_length - sizeof(*avp); @@ -185,7 +200,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) if (dlen < 4) { wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " "underflow"); - return -1; + goto fail; } vendor_id = be_to_host32(* (u32 *) dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", @@ -204,7 +219,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); - return -1; + goto fail; } memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; @@ -215,9 +230,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); - free(parse->eap); - parse->eap = NULL; - return -1; + goto fail; } memcpy(neweap + parse->eap_len, dpos, dlen); parse->eap = neweap; @@ -281,7 +294,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " "mandatory AVP code %d vendor_id %d - " "dropped", (int) avp_code, (int) vendor_id); - return -1; + goto fail; } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " "AVP code %d vendor_id %d", @@ -294,6 +307,65 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) } return 0; + +fail: + free(parse->eap); + parse->eap = NULL; + return -1; +} + + +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ + struct tls_keys keys; + u8 *challenge, *rnd; + + if (data->ttls_version == 0) { + return eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); + } + + memset(&keys, 0, sizeof(keys)); + if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL || + keys.inner_secret == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " + "client random, or server random to derive " + "implicit challenge"); + return NULL; + } + + rnd = malloc(keys.client_random_len + keys.server_random_len); + challenge = malloc(len); + if (rnd == NULL || challenge == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " + "challenge derivation"); + free(rnd); + free(challenge); + return NULL; + } + memcpy(rnd, keys.server_random, keys.server_random_len); + memcpy(rnd + keys.server_random_len, keys.client_random, + keys.client_random_len); + + if (tls_prf(keys.inner_secret, keys.inner_secret_len, + "inner application challenge", rnd, + keys.client_random_len + keys.server_random_len, + challenge, len)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " + "challenge"); + free(rnd); + free(challenge); + return NULL; + } + + free(rnd); + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", + challenge, len); + + return challenge; } @@ -301,13 +373,31 @@ static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->ttls_version = EAP_TTLS_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d", + data->force_version); + data->ttls_version = data->force_version; + } data->state = START; + if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && + data->ttls_version > 0) { + if (data->force_version > 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " + "TLS library does not support TLS/IA.", + data->force_version); + eap_ttls_reset(sm, data); + return NULL; + } + data->ttls_version = 0; + } + if (eap_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); @@ -456,23 +546,24 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, int id, size_t *reqDataLen) { u8 *req, *encr_req, *pos, *end; + int ret; size_t req_len; - int i; pos = req = malloc(100); if (req == NULL) return NULL; - end = req + 200; + end = req + 100; if (data->mschapv2_resp_ok) { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; - pos += snprintf((char *) pos, end - pos, "S="); - for (i = 0; i < sizeof(data->mschapv2_auth_response); i++) { - pos += snprintf((char *) pos, end - pos, "%02X", - data->mschapv2_auth_response[i]); - } + ret = snprintf((char *) pos, end - pos, "S="); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex_uppercase( + (char *) pos, end - pos, data->mschapv2_auth_response, + sizeof(data->mschapv2_auth_response)); } else { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, RADIUS_VENDOR_ID_MICROSOFT, 1, 6); @@ -492,6 +583,43 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, } +static u8 * eap_ttls_build_phase_finished(struct eap_sm *sm, + struct eap_ttls_data *data, + int id, int final, + size_t *reqDataLen) +{ + int len; + struct eap_hdr *req; + u8 *pos; + const int max_len = 300; + + len = sizeof(struct eap_hdr) + 2 + max_len; + req = malloc(len); + if (req == NULL) + return NULL; + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_TTLS; + *pos++ = data->ttls_version; + + len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, + data->ssl.conn, + final, pos, max_len); + if (len < 0) { + free(req); + return NULL; + } + + *reqDataLen = sizeof(struct eap_hdr) + 2 + len; + req->length = host_to_be16(*reqDataLen); + + return (u8 *) req; +} + + static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, size_t *reqDataLen) { @@ -507,6 +635,9 @@ static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, case PHASE2_MSCHAPV2_RESP: return eap_ttls_build_phase2_mschapv2(sm, data, id, reqDataLen); + case PHASE_FINISHED: + return eap_ttls_build_phase_finished(sm, data, id, 1, + reqDataLen); default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); @@ -520,12 +651,11 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, { struct eap_hdr *resp; u8 *pos; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); return TRUE; } @@ -534,6 +664,37 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, } +static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *key, size_t key_len) +{ + u8 *buf; + size_t buf_len; + int ret; + + if (key) { + buf_len = 2 + key_len; + buf = malloc(buf_len); + if (buf == NULL) + return -1; + WPA_PUT_BE16(buf, key_len); + memcpy(buf + 2, key, key_len); + } else { + buf = NULL; + buf_len = 0; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " + "secret permutation", buf, buf_len); + ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, + data->ssl.conn, + buf, buf_len); + free(buf); + + return ret; +} + + static void eap_ttls_process_phase2_pap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *user_password, @@ -541,9 +702,9 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, { /* TODO: add support for verifying that the user entry accepts * EAP-TTLS/PAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No user password " - "configured"); + if (!sm->user || !sm->user->password || sm->user->password_hash) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " + "password configured"); eap_ttls_state(data, FAILURE); return; } @@ -557,7 +718,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } @@ -585,15 +747,15 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, /* TODO: add support for verifying that the user entry accepts * EAP-TTLS/CHAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No user password " - "configured"); + if (!sm->user || !sm->user->password || sm->user->password_hash) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " + "password configured"); eap_ttls_state(data, FAILURE); return; } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " "challenge from TLS data"); @@ -621,7 +783,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -656,8 +819,8 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " "challenge from TLS data"); @@ -674,12 +837,16 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, } free(chal); - nt_challenge_response(challenge, sm->user->password, - sm->user->password_len, nt_response); + if (sm->user->password_hash) + challenge_response(challenge, sm->user->password, nt_response); + else + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response); if (memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -697,10 +864,9 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, size_t challenge_len, u8 *response, size_t response_len) { - u8 *chal, *username, nt_response[24], *pos, *rx_resp, *peer_challenge, + u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge, *auth_challenge; - size_t username_len; - int i; + size_t username_len, i; if (challenge == NULL || response == NULL || challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || @@ -727,7 +893,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, * (if present). */ username = sm->identity; username_len = sm->identity_len; - pos = username; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; @@ -736,8 +901,8 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " "challenge from TLS data"); @@ -764,25 +929,62 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - sm->user->password, sm->user->password_len, - nt_response); + if (sm->user->password_hash) { + generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + nt_response); + } else { + generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + nt_response); + } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; if (memcmp(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; + if (data->ttls_version > 0) { + const u8 *pw_hash; + u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16]; + u8 session_key[2 * MSCHAPV2_KEY_LEN]; + + if (sm->user->password_hash) + pw_hash = sm->user->password; + else { + nt_password_hash(sm->user->password, + sm->user->password_len, + pw_hash_buf); + pw_hash = pw_hash_buf; + } + hash_nt_password_hash(pw_hash, pw_hash_hash); + get_master_key(pw_hash_hash, nt_response, master_key); + get_asymetric_start_key(master_key, session_key, + MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(master_key, + session_key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 0); + eap_ttls_ia_permute_inner_secret(sm, data, + session_key, + sizeof(session_key)); + } - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - auth_challenge, - username, username_len, - nt_response, - data->mschapv2_auth_response); - + if (sm->user->password_hash) { + generate_authenticator_response_pwhash( + sm->user->password, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } else { + generate_authenticator_response( + sm->user->password, sm->user->password_len, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " "NT-Response"); @@ -798,14 +1000,16 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, static int eap_ttls_phase2_eap_init(struct eap_sm *sm, - struct eap_ttls_data *data, u8 eap_type) + struct eap_ttls_data *data, + EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_sm_get_eap_methods(eap_type); + data->phase2_method = eap_sm_get_eap_methods(EAP_VENDOR_IETF, + eap_type); if (!data->phase2_method) return -1; @@ -824,6 +1028,8 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, struct eap_hdr *hdr; u8 *pos; size_t left; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " @@ -833,17 +1039,17 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); - left = in_len - sizeof(*hdr); if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); 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] != + sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { - next_type = - sm->user->methods[sm->user_eap_method_index++]; + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); eap_ttls_phase2_eap_init(sm, data, next_type); @@ -853,19 +1059,18 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, return; } - if (data->phase2_method->check(sm, data->phase2_priv, in_data, - in_len)) { + if (m->check(sm, priv, in_data, in_len)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " "ignore the packet"); return; } - data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); + m->process(sm, priv, in_data, in_len); - if (!data->phase2_method->isDone(sm, data->phase2_priv)) + if (!m->isDone(sm, priv)) return; - if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + if (!m->isSuccess(sm, priv)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); eap_ttls_state(data, FAILURE); return; @@ -883,12 +1088,22 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, } eap_ttls_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0]; + next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); break; case PHASE2_METHOD: - eap_ttls_state(data, SUCCESS); + if (data->ttls_version > 0) { + if (m->getKey) { + u8 *key; + size_t key_len; + key = m->getKey(sm, priv, &key_len); + eap_ttls_ia_permute_inner_secret(sm, data, + key, key_len); + } + eap_ttls_state(data, PHASE_FINISHED); + } else + eap_ttls_state(data, SUCCESS); break; case FAILURE: break; @@ -956,8 +1171,9 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, u8 *in_data, size_t in_len) { u8 *in_decrypted; - int buf_len, len_decrypted, res; + int len_decrypted, res; struct eap_ttls_avp parse; + size_t buf_len; wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); @@ -993,6 +1209,23 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } + if (data->state == PHASE_FINISHED) { + if (len_decrypted == 0 && + tls_connection_ia_final_phase_finished(sm->ssl_ctx, + data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished " + "received"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid " + "FinalPhaseFinished"); + eap_ttls_state(data, FAILURE); + } + + free(in_decrypted); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", in_decrypted, len_decrypted); @@ -1075,8 +1308,18 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, "use version %d", peer_version, data->ttls_version, peer_version); data->ttls_version = peer_version; - } + + if (data->ttls_version > 0 && !data->tls_ia_configured) { + if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable " + "TLS/IA"); + eap_ttls_state(data, FAILURE); + return; + } + data->tls_ia_configured = 1; + } + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { if (left < 4) { wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " @@ -1109,13 +1352,15 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, break; case PHASE2_START: case PHASE2_METHOD: + case PHASE_FINISHED: eap_ttls_process_phase2(sm, data, resp, pos, left); break; case PHASE2_MSCHAPV2_RESP: if (data->mschapv2_resp_ok && left == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? + PHASE_FINISHED : SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1148,6 +1393,54 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) } +static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct tls_keys keys; + u8 *rnd, *key; + + memset(&keys, 0, sizeof(keys)); + if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL || + keys.inner_secret == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " + "client random, or server random to derive keying " + "material"); + return NULL; + } + + rnd = malloc(keys.client_random_len + keys.server_random_len); + key = malloc(EAP_TLS_KEY_LEN); + if (rnd == NULL || key == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); + free(rnd); + free(key); + return NULL; + } + memcpy(rnd, keys.client_random, keys.client_random_len); + memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.inner_secret, keys.inner_secret_len, + "ttls v1 keying material", rnd, keys.client_random_len + + keys.server_random_len, key, EAP_TLS_KEY_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); + free(rnd); + free(key); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", + rnd, keys.client_random_len + keys.server_random_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", + keys.inner_secret, keys.inner_secret_len); + + free(rnd); + + return key; +} + + static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; @@ -1156,13 +1449,18 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); + if (data->ttls_version == 0) { + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + } else { + eapKeyData = eap_ttls_v1_derive_key(sm, data); + } + if (eapKeyData) { *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); } @@ -1178,16 +1476,27 @@ static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_ttls = +int eap_server_ttls_register(void) { - .method = EAP_TYPE_TTLS, - .name = "TTLS", - .init = eap_ttls_init, - .reset = eap_ttls_reset, - .buildReq = eap_ttls_buildReq, - .check = eap_ttls_check, - .process = eap_ttls_process, - .isDone = eap_ttls_isDone, - .getKey = eap_ttls_getKey, - .isSuccess = eap_ttls_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->reset = eap_ttls_reset; + eap->buildReq = eap_ttls_buildReq; + eap->check = eap_ttls_check; + eap->process = eap_ttls_process; + eap->isDone = eap_ttls_isDone; + eap->getKey = eap_ttls_getKey; + eap->isSuccess = eap_ttls_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} |