/* * EAP peer method: EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) * Copyright (c) 2004-2006, 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 * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include "common.h" #include "eap_i.h" #include "eap_tls_common.h" #include "config_ssid.h" #include "ms_funcs.h" #include "sha1.h" #include "crypto.h" #include "tls.h" #include "eap_ttls.h" /* 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_deinit(struct eap_sm *sm, void *priv); struct eap_ttls_data { struct eap_ssl_data ssl; int ssl_initialized; int ttls_version, force_ttls_version; const struct eap_method *phase2_method; void *phase2_priv; int phase2_success; int phase2_start; enum { EAP_TTLS_PHASE2_EAP, EAP_TTLS_PHASE2_MSCHAPV2, EAP_TTLS_PHASE2_MSCHAP, EAP_TTLS_PHASE2_PAP, EAP_TTLS_PHASE2_CHAP } phase2_type; struct eap_method_type phase2_eap_type; struct eap_method_type *phase2_eap_types; size_t num_phase2_eap_types; u8 auth_response[20]; int auth_response_valid; u8 ident; int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; u8 *pending_phase2_req; size_t pending_phase2_req_len; }; static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; struct wpa_ssid *config = eap_get_config(sm); char *selected; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; data->force_ttls_version = -1; selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; if (config && config->phase1) { char *pos = os_strstr(config->phase1, "ttlsver="); if (pos) { data->force_ttls_version = atoi(pos + 8); data->ttls_version = data->force_ttls_version; wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version " "%d", data->force_ttls_version); } } if (config && config->phase2) { if (os_strstr(config->phase2, "autheap=")) { selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { selected = "MSCHAPV2"; data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; } else if (os_strstr(config->phase2, "auth=MSCHAP")) { selected = "MSCHAP"; data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; } else if (os_strstr(config->phase2, "auth=PAP")) { selected = "PAP"; data->phase2_type = EAP_TTLS_PHASE2_PAP; } else if (os_strstr(config->phase2, "auth=CHAP")) { selected = "CHAP"; data->phase2_type = EAP_TTLS_PHASE2_CHAP; } } wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { if (config && config->phase2) { char *start, *pos, *buf; struct eap_method_type *methods = NULL, *_methods; u8 method; size_t num_methods = 0; start = buf = os_strdup(config->phase2); if (buf == NULL) { eap_ttls_deinit(sm, data); return NULL; } while (start && *start != '\0') { int vendor; pos = os_strstr(start, "autheap="); if (pos == NULL) break; if (start != pos && *(pos - 1) != ' ') { start = pos + 8; continue; } start = pos + 8; pos = os_strchr(start, ' '); if (pos) *pos++ = '\0'; method = eap_get_phase2_type(start, &vendor); if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { wpa_printf(MSG_ERROR, "EAP-TTLS: " "Unsupported Phase2 EAP " "method '%s'", start); } else { num_methods++; _methods = os_realloc( methods, num_methods * sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); eap_ttls_deinit(sm, data); return NULL; } methods = _methods; methods[num_methods - 1].vendor = vendor; methods[num_methods - 1].method = method; } start = pos; } os_free(buf); data->phase2_eap_types = methods; data->num_phase2_eap_types = num_methods; } if (data->phase2_eap_types == NULL) { data->phase2_eap_types = eap_get_phase2_types( config, &data->num_phase2_eap_types); } if (data->phase2_eap_types == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS: No Phase2 EAP method " "available"); eap_ttls_deinit(sm, data); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase2 EAP types", (u8 *) data->phase2_eap_types, data->num_phase2_eap_types * sizeof(struct eap_method_type)); data->phase2_eap_type.vendor = EAP_VENDOR_IETF; data->phase2_eap_type.method = EAP_TYPE_NONE; } if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && data->ttls_version > 0) { if (data->force_ttls_version > 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " "TLS library does not support TLS/IA.", data->force_ttls_version); eap_ttls_deinit(sm, data); return NULL; } data->ttls_version = 0; } return data; } static void eap_ttls_deinit(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; if (data == NULL) return; if (data->phase2_priv && data->phase2_method) data->phase2_method->deinit(sm, data->phase2_priv); os_free(data->phase2_eap_types); if (data->ssl_initialized) eap_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); os_free(data->pending_phase2_req); os_free(data); } static int eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, int id, const u8 *plain, size_t plain_len, u8 **out_data, size_t *out_len) { int res; u8 *pos; struct eap_hdr *resp; /* TODO: add support for fragmentation, if needed. This will need to * add TLS Message Length field, if the frame is fragmented. */ resp = os_malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); if (resp == NULL) return -1; resp->code = EAP_CODE_RESPONSE; resp->identifier = id; pos = (u8 *) (resp + 1); *pos++ = EAP_TYPE_TTLS; *pos++ = data->ttls_version; res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, plain, plain_len, pos, data->ssl.tls_out_limit); if (res < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " "data"); os_free(resp); return -1; } *out_len = sizeof(struct eap_hdr) + 2 + res; resp->length = host_to_be16(*out_len); *out_data = (u8 *) resp; return 0; } static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, size_t len) { struct ttls_avp_vendor *avp; u8 flags; size_t hdrlen; avp = (struct ttls_avp_vendor *) avphdr; flags = mandatory ? AVP_FLAGS_MANDATORY : 0; if (vendor_id) { flags |= AVP_FLAGS_VENDOR; hdrlen = sizeof(*avp); avp->vendor_id = host_to_be32(vendor_id); } else { hdrlen = sizeof(struct ttls_avp); } avp->avp_code = host_to_be32(avp_code); avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); return avphdr + hdrlen; } static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, u32 vendor_id, int mandatory, u8 *data, size_t len) { u8 *pos; pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); os_memcpy(pos, data, len); pos += len; AVP_PAD(start, pos); return pos; } static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, int mandatory) { u8 *avp, *pos; avp = os_malloc(sizeof(struct ttls_avp) + *resp_len + 4); if (avp == NULL) { os_free(*resp); *resp = NULL; *resp_len = 0; return -1; } pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len); os_memcpy(pos, *resp, *resp_len); pos += *resp_len; AVP_PAD(avp, pos); os_free(*resp); *resp = avp; *resp_len = pos - avp; return 0; } 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 = os_malloc(buf_len); if (buf == NULL) return -1; WPA_PUT_BE16(buf, key_len); os_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); os_free(buf); return ret; } static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { os_free(data->key_data); data->key_data = eap_tls_derive_key(sm, &data->ssl, "ttls keying material", EAP_TLS_KEY_LEN); if (!data->key_data) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); return -1; } wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); return 0; } static int eap_ttls_v1_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { struct tls_keys keys; u8 *rnd; os_free(data->key_data); data->key_data = NULL; os_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 -1; } rnd = os_malloc(keys.client_random_len + keys.server_random_len); data->key_data = os_malloc(EAP_TLS_KEY_LEN); if (rnd == NULL || data->key_data == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); os_free(rnd); os_free(data->key_data); data->key_data = NULL; return -1; } os_memcpy(rnd, keys.client_random, keys.client_random_len); os_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, data->key_data, EAP_TLS_KEY_LEN)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); os_free(rnd); os_free(data->key_data); data->key_data = NULL; return -1; } 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); os_free(rnd); wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); return 0; } 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); } os_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 = os_malloc(keys.client_random_len + keys.server_random_len); challenge = os_malloc(len); if (rnd == NULL || challenge == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " "challenge derivation"); os_free(rnd); os_free(challenge); return NULL; } os_memcpy(rnd, keys.server_random, keys.server_random_len); os_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"); os_free(rnd); os_free(challenge); return NULL; } os_free(rnd); wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", challenge, len); return challenge; } static int eap_ttls_phase2_nak(struct eap_ttls_data *data, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { struct eap_hdr *resp_hdr; u8 *pos = (u8 *) (hdr + 1); size_t i; /* TODO: add support for expanded Nak */ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 Request: Nak type=%d", *pos); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Allowed Phase2 EAP types", (u8 *) data->phase2_eap_types, data->num_phase2_eap_types * sizeof(struct eap_method_type)); *resp_len = sizeof(struct eap_hdr) + 1; *resp = os_malloc(*resp_len + data->num_phase2_eap_types); if (*resp == NULL) return -1; resp_hdr = (struct eap_hdr *) (*resp); resp_hdr->code = EAP_CODE_RESPONSE; resp_hdr->identifier = hdr->identifier; pos = (u8 *) (resp_hdr + 1); *pos++ = EAP_TYPE_NAK; for (i = 0; i < data->num_phase2_eap_types; i++) { if (data->phase2_eap_types[i].vendor == EAP_VENDOR_IETF && data->phase2_eap_types[i].method < 256) { (*resp_len)++; *pos++ = data->phase2_eap_types[i].method; } } resp_hdr->length = host_to_be16(*resp_len); return 0; } static int eap_ttls_phase2_request_eap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { size_t len = be_to_host16(hdr->length); u8 *pos; struct eap_method_ret iret; struct wpa_ssid *config = eap_get_config(sm); if (len <= sizeof(struct eap_hdr)) { wpa_printf(MSG_INFO, "EAP-TTLS: too short " "Phase 2 request (len=%lu)", (unsigned long) len); return -1; } pos = (u8 *) (hdr + 1); wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); switch (*pos) { case EAP_TYPE_IDENTITY: *resp = eap_sm_buildIdentity(sm, hdr->identifier, resp_len, 1); break; default: if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && data->phase2_eap_type.method == EAP_TYPE_NONE) { size_t i; for (i = 0; i < data->num_phase2_eap_types; i++) { if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || data->phase2_eap_types[i].method != *pos) continue; data->phase2_eap_type.vendor = data->phase2_eap_types[i].vendor; data->phase2_eap_type.method = data->phase2_eap_types[i].method; wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " "Phase 2 EAP vendor %d method %d", data->phase2_eap_type.vendor, data->phase2_eap_type.method); break; } } if (*pos != data->phase2_eap_type.method || *pos == EAP_TYPE_NONE) { if (eap_ttls_phase2_nak(data, hdr, resp, resp_len)) return -1; break; } if (data->phase2_priv == NULL) { data->phase2_method = eap_sm_get_eap_methods( EAP_VENDOR_IETF, *pos); if (data->phase2_method) { sm->init_phase2 = 1; sm->mschapv2_full_key = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; sm->mschapv2_full_key = 0; } } if (data->phase2_priv == NULL || data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " "Phase 2 EAP method %d", *pos); return -1; } os_memset(&iret, 0, sizeof(iret)); *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, (u8 *) hdr, len, resp_len); if ((iret.methodState == METHOD_DONE || iret.methodState == METHOD_MAY_CONT) && (iret.decision == DECISION_UNCOND_SUCC || iret.decision == DECISION_COND_SUCC || iret.decision == DECISION_FAIL)) { ret->methodState = iret.methodState; ret->decision = iret.decision; } if (data->ttls_version > 0) { const struct eap_method *m = data->phase2_method; void *priv = data->phase2_priv; /* TTLSv1 requires TLS/IA FinalPhaseFinished */ if (ret->decision == DECISION_UNCOND_SUCC) ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_CONT; if (ret->decision == DECISION_COND_SUCC && m->isKeyAvailable && m->getKey && m->isKeyAvailable(sm, priv)) { u8 *key; size_t key_len; key = m->getKey(sm, priv, &key_len); if (key) { eap_ttls_ia_permute_inner_secret( sm, data, key, key_len); os_free(key); } } } break; } if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp)) { return 0; } if (*resp == NULL) return -1; wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", *resp, *resp_len); return eap_ttls_avp_encapsulate(resp, resp_len, RADIUS_ATTR_EAP_MESSAGE, 1); } static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos, *challenge, *username, *peer_challenge; size_t username_len, i; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ username = config->identity; username_len = config->identity_len; pos = username; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; username += i + 1; break; } } pos = buf = os_malloc(config->identity_len + 1000); if (buf == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); return -1; } /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, config->identity, config->identity_len); /* MS-CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { os_free(buf); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; } peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); /* MS-CHAP2-Response */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, RADIUS_VENDOR_ID_MICROSOFT, 1, EAP_TTLS_MSCHAPV2_RESPONSE_LEN); data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: implicit auth_challenge", challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: peer_challenge", peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 username", username, username_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 password", config->password, config->password_len); generate_nt_response(challenge, peer_challenge, username, username_len, config->password, config->password_len, pos); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 response", pos, 24); generate_authenticator_response(config->password, config->password_len, peer_challenge, challenge, username, username_len, pos, data->auth_response); data->auth_response_valid = 1; if (data->ttls_version > 0) { u8 pw_hash[16], pw_hash_hash[16], master_key[16]; u8 session_key[2 * MSCHAPV2_KEY_LEN]; nt_password_hash(config->password, config->password_len, pw_hash); hash_nt_password_hash(pw_hash, pw_hash_hash); get_master_key(pw_hash_hash, pos /* 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)); } pos += 24; os_free(challenge); AVP_PAD(buf, pos); *resp = buf; *resp_len = pos - buf; if (sm->workaround && data->ttls_version == 0) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " "allow success without tunneled response"); ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_COND_SUCC; } return 0; } static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos, *challenge; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); pos = buf = os_malloc(config->identity_len + 1000); if (buf == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to allocate memory"); return -1; } /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, config->identity, config->identity_len); /* MS-CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { os_free(buf); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " "implicit challenge"); return -1; } pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); /* MS-CHAP-Response */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, RADIUS_VENDOR_ID_MICROSOFT, 1, EAP_TTLS_MSCHAP_RESPONSE_LEN); data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 1; /* Flags: Use NT style passwords */ os_memset(pos, 0, 24); /* LM-Response */ pos += 24; nt_challenge_response(challenge, config->password, config->password_len, pos); /* NT-Response */ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", config->password, config->password_len); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); pos += 24; os_free(challenge); AVP_PAD(buf, pos); *resp = buf; *resp_len = pos - buf; if (data->ttls_version > 0) { /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, * so do not allow connection to be terminated yet. */ ret->methodState = METHOD_CONT; ret->decision = DECISION_COND_SUCC; } else { /* EAP-TTLS/MSCHAP does not provide tunneled success * notification, so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; } return 0; } static int eap_ttls_phase2_request_pap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos; size_t pad; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); pos = buf = os_malloc(config->identity_len + config->password_len + 100); if (buf == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/PAP: Failed to allocate memory"); return -1; } /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, config->identity, config->identity_len); /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts * the data, so no separate encryption is used in the AVP itself. * However, the password is padded to obfuscate its length. */ pad = (16 - (config->password_len & 15)) & 15; pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, config->password_len + pad); os_memcpy(pos, config->password, config->password_len); pos += config->password_len; os_memset(pos, 0, pad); pos += pad; AVP_PAD(buf, pos); *resp = buf; *resp_len = pos - buf; if (data->ttls_version > 0) { /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, * so do not allow connection to be terminated yet. */ ret->methodState = METHOD_CONT; ret->decision = DECISION_COND_SUCC; } else { /* EAP-TTLS/PAP does not provide tunneled success notification, * so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; } return 0; } static int eap_ttls_phase2_request_chap(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, u8 **resp, size_t *resp_len) { struct wpa_ssid *config = eap_get_config(sm); u8 *buf, *pos, *challenge; const u8 *addr[3]; size_t len[3]; wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); pos = buf = os_malloc(config->identity_len + 1000); if (buf == NULL) { wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to allocate memory"); return -1; } /* User-Name */ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, config->identity, config->identity_len); /* CHAP-Challenge */ challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { os_free(buf); wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " "implicit challenge"); return -1; } pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); /* CHAP-Password */ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, 1 + EAP_TTLS_CHAP_PASSWORD_LEN); data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; *pos++ = data->ident; /* MD5(Ident + Password + Challenge) */ addr[0] = &data->ident; len[0] = 1; addr[1] = config->password; len[1] = config->password_len; addr[2] = challenge; len[2] = EAP_TTLS_CHAP_CHALLENGE_LEN; md5_vector(3, addr, len, pos); wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", config->identity, config->identity_len); wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", config->password, config->password_len); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", pos, EAP_TTLS_CHAP_PASSWORD_LEN); pos += EAP_TTLS_CHAP_PASSWORD_LEN; os_free(challenge); AVP_PAD(buf, pos); *resp = buf; *resp_len = pos - buf; if (data->ttls_version > 0) { /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, * so do not allow connection to be terminated yet. */ ret->methodState = METHOD_CONT; ret->decision = DECISION_COND_SUCC; } else { /* EAP-TTLS/CHAP does not provide tunneled success * notification, so assume that Phase2 succeeds. */ ret->methodState = METHOD_DONE; ret->decision = DECISION_COND_SUCC; } return 0; } static int eap_ttls_phase2_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, struct eap_hdr *hdr, u8 **resp, size_t *resp_len) { int res = 0; size_t len; if (data->phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || data->phase2_type == EAP_TTLS_PHASE2_MSCHAP || data->phase2_type == EAP_TTLS_PHASE2_PAP || data->phase2_type == EAP_TTLS_PHASE2_CHAP) { if (eap_get_config_identity(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Identity not configured"); eap_sm_request_identity(sm); if (eap_get_config_password(sm, &len) == NULL) eap_sm_request_password(sm); return 0; } if (eap_get_config_password(sm, &len) == NULL) { wpa_printf(MSG_INFO, "EAP-TTLS: Password not configured"); eap_sm_request_password(sm); return 0; } } switch (data->phase2_type) { case EAP_TTLS_PHASE2_EAP: res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp, resp_len); break; case EAP_TTLS_PHASE2_MSCHAPV2: res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp, resp_len); break; case EAP_TTLS_PHASE2_MSCHAP: res = eap_ttls_phase2_request_mschap(sm, data, ret, resp, resp_len); break; case EAP_TTLS_PHASE2_PAP: res = eap_ttls_phase2_request_pap(sm, data, ret, resp, resp_len); break; case EAP_TTLS_PHASE2_CHAP: res = eap_ttls_phase2_request_chap(sm, data, ret, resp, resp_len); break; default: wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); res = -1; break; } if (res < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } return res; } 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 = os_malloc(len); if (req == NULL) return NULL; req->code = EAP_CODE_RESPONSE; 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) { os_free(req); return NULL; } *reqDataLen = sizeof(struct eap_hdr) + 2 + len; req->length = host_to_be16(*reqDataLen); return (u8 *) req; } static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, const u8 *in_data, size_t in_len, u8 **out_data, size_t *out_len) { u8 *in_decrypted = NULL, *pos; int res, retval = 0; struct eap_hdr *hdr = NULL; u8 *resp = NULL, *mschapv2 = NULL, *eapdata = NULL; size_t resp_len, eap_len = 0, len_decrypted = 0, len, buf_len, left; struct ttls_avp *avp; u8 recv_response[20]; int mschapv2_error = 0; struct wpa_ssid *config = eap_get_config(sm); const u8 *msg; size_t msg_len; int need_more_input; wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); if (data->pending_phase2_req) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " "skip decryption and use old data"); /* Clear TLS reassembly state. */ os_free(data->ssl.tls_in); data->ssl.tls_in = NULL; data->ssl.tls_in_len = 0; data->ssl.tls_in_left = 0; data->ssl.tls_in_total = 0; in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; len_decrypted = data->pending_phase2_req_len; if (data->pending_phase2_req_len == 0) { os_free(in_decrypted); in_decrypted = NULL; goto fake_req_identity; } goto continue_req; } if (in_len == 0 && data->phase2_start) { data->phase2_start = 0; /* EAP-TTLS does not use Phase2 on fast re-auth; this must be * done only if TLS part was indeed resuming a previous * session. Most Authentication Servers terminate EAP-TTLS * before reaching this point, but some do not. Make * wpa_supplicant stop phase 2 here, if needed. */ if (data->reauth && tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " "skip phase 2"); *out_data = eap_tls_build_ack(&data->ssl, out_len, req->identifier, EAP_TYPE_TTLS, 0); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; data->phase2_success = 1; return 0; } fake_req_identity: wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " "Phase 2 - use fake EAP-Request Identity"); buf_len = sizeof(*hdr) + 1; in_decrypted = os_malloc(buf_len); if (in_decrypted == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " "memory for fake EAP-Identity Request"); retval = -1; goto done; } hdr = (struct eap_hdr *) in_decrypted; hdr->code = EAP_CODE_REQUEST; hdr->identifier = 0; hdr->length = host_to_be16(sizeof(*hdr) + 1); in_decrypted[sizeof(*hdr)] = EAP_TYPE_IDENTITY; goto process_eap; } msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len, &msg_len, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; buf_len = in_len; if (data->ssl.tls_in_total > buf_len) buf_len = data->ssl.tls_in_total; 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-TTLS: failed to allocate memory " "for decryption"); retval = -1; goto done; } res = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, msg, msg_len, in_decrypted, buf_len); os_free(data->ssl.tls_in); data->ssl.tls_in = NULL; data->ssl.tls_in_len = 0; if (res < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); retval = -1; goto done; } len_decrypted = res; if (data->ttls_version > 0 && len_decrypted == 0 && tls_connection_ia_final_phase_finished(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received"); wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication " "succeeded"); ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; data->phase2_success = 1; *out_data = eap_ttls_build_phase_finished(sm, data, req->identifier, 1, out_len); eap_ttls_v1_derive_key(sm, data); goto done; } continue_req: data->phase2_start = 0; wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", in_decrypted, len_decrypted); if (len_decrypted < sizeof(struct ttls_avp)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" " len=%lu expected %lu or more - dropped", (unsigned long) len_decrypted, (unsigned long) sizeof(struct ttls_avp)); retval = -1; goto done; } /* Parse AVPs */ pos = in_decrypted; left = len_decrypted; mschapv2 = NULL; while (left > 0) { u32 avp_code, avp_length, vendor_id = 0; u8 avp_flags, *dpos; size_t pad, dlen; avp = (struct ttls_avp *) pos; avp_code = be_to_host32(avp->avp_code); avp_length = be_to_host32(avp->avp_length); avp_flags = (avp_length >> 24) & 0xff; avp_length &= 0xffffff; 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) { wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " "(len=%d, left=%lu) - dropped", (int) avp_length, (unsigned long) left); retval = -1; goto done; } if (avp_length < sizeof(*avp)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " "%d", avp_length); retval = -1; goto done; } dpos = (u8 *) (avp + 1); dlen = avp_length - sizeof(*avp); if (avp_flags & AVP_FLAGS_VENDOR) { if (dlen < 4) { wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " "underflow"); retval = -1; goto done; } vendor_id = be_to_host32(* (u32 *) dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", (int) vendor_id); dpos += 4; dlen -= 4; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (eapdata == NULL) { eapdata = os_malloc(dlen); if (eapdata == NULL) { retval = -1; wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto done; } os_memcpy(eapdata, dpos, dlen); eap_len = dlen; } else { u8 *neweap = os_realloc(eapdata, eap_len + dlen); if (neweap == NULL) { retval = -1; wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto done; } os_memcpy(neweap + eap_len, dpos, dlen); eapdata = neweap; eap_len += dlen; } } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { /* This is an optional message that can be displayed to * the user. */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", dpos, dlen); } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: " "MS-CHAP2-Success", dpos, dlen); if (dlen != 43) { wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " "MS-CHAP2-Success length " "(len=%lu, expected 43)", (unsigned long) dlen); retval = -1; break; } mschapv2 = dpos; } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: " "MS-CHAP-Error", dpos, dlen); mschapv2_error = 1; } else if (avp_flags & AVP_FLAGS_MANDATORY) { wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " "mandatory AVP code %d vendor_id %d - " "dropped", (int) avp_code, (int) vendor_id); retval = -1; goto done; } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " "AVP code %d vendor_id %d", (int) avp_code, (int) vendor_id); } pad = (4 - (avp_length & 3)) & 3; pos += avp_length + pad; if (left < avp_length + pad) left = 0; else left -= avp_length + pad; } switch (data->phase2_type) { case EAP_TTLS_PHASE2_EAP: if (eapdata == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in " "the packet - dropped"); retval = -1; goto done; } wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", eapdata, eap_len); hdr = (struct eap_hdr *) eapdata; if (eap_len < sizeof(*hdr)) { wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 " "EAP frame (len=%lu, expected %lu or more) " "- dropped", (unsigned long) eap_len, (unsigned long) sizeof(*hdr)); retval = -1; goto done; } len = be_to_host16(hdr->length); if (len > eap_len) { wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in " "Phase 2 EAP frame (EAP hdr len=%lu, EAP " "data len in AVP=%lu)", (unsigned long) len, (unsigned long) eap_len); retval = -1; goto done; } wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " "identifier=%d length=%lu", hdr->code, hdr->identifier, (unsigned long) len); process_eap: switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_ttls_phase2_request(sm, data, ret, req, hdr, &resp, &resp_len)) { wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 " "Request processing failed"); retval = -1; goto done; } break; default: wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " "Phase 2 EAP header", hdr->code); retval = -1; break; } break; case EAP_TTLS_PHASE2_MSCHAPV2: if (mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; *out_data = eap_tls_build_ack(&data->ssl, out_len, req->identifier, EAP_TYPE_TTLS, 0); break; } if (mschapv2 == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success" " AVP received for Phase2 MSCHAPV2"); retval = -1; break; } if (mschapv2[0] != data->ident) { wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch " "for Phase 2 MSCHAPV2 (received Ident " "0x%02x, expected 0x%02x)", mschapv2[0], data->ident); retval = -1; break; } if (!data->auth_response_valid || mschapv2[1] != 'S' || mschapv2[2] != '=' || hexstr2bin((char *) (mschapv2 + 3), recv_response, 20) || os_memcmp(data->auth_response, recv_response, 20) != 0) { wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid " "authenticator response in Phase 2 " "MSCHAPV2 success request"); retval = -1; break; } wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " "authentication succeeded"); if (data->ttls_version > 0) { /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report * success, so do not allow connection to be terminated * yet. */ ret->methodState = METHOD_CONT; ret->decision = DECISION_COND_SUCC; } else { ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; data->phase2_success = 1; } /* Reply with empty data; authentication server will reply * with EAP-Success after this. */ retval = 1; goto done; case EAP_TTLS_PHASE2_MSCHAP: case EAP_TTLS_PHASE2_PAP: case EAP_TTLS_PHASE2_CHAP: /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled * requests to the supplicant */ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " "tunneled data"); retval = -1; break; } if (resp) { wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", resp, resp_len); if (eap_ttls_encrypt(sm, data, req->identifier, resp, resp_len, out_data, out_len)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt " "a Phase 2 frame"); } os_free(resp); } else if (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password) { os_free(data->pending_phase2_req); data->pending_phase2_req = os_malloc(len_decrypted); if (data->pending_phase2_req) { os_memcpy(data->pending_phase2_req, in_decrypted, len_decrypted); data->pending_phase2_req_len = len_decrypted; } } done: os_free(in_decrypted); os_free(eapdata); if (retval < 0) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; } return retval; } static u8 * eap_ttls_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const u8 *reqData, size_t reqDataLen, size_t *respDataLen) { const struct eap_hdr *req; size_t left; int res; u8 flags, *resp, id; const u8 *pos; struct eap_ttls_data *data = priv; struct wpa_ssid *config = eap_get_config(sm); pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, reqData, reqDataLen, &left, &flags); if (pos == NULL) return NULL; req = (const struct eap_hdr *) reqData; id = req->identifier; if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " "ver=%d)", flags & EAP_PEAP_VERSION_MASK, data->ttls_version); if ((flags & EAP_PEAP_VERSION_MASK) < data->ttls_version) data->ttls_version = flags & EAP_PEAP_VERSION_MASK; if (data->force_ttls_version >= 0 && data->force_ttls_version != data->ttls_version) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " "forced TTLS version %d", data->force_ttls_version); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; return NULL; } wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d", data->ttls_version); if (data->ttls_version > 0) data->ssl.tls_ia = 1; if (!data->ssl_initialized && eap_tls_ssl_init(sm, &data->ssl, config)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize " "SSL."); return NULL; } data->ssl_initialized = 1; wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); /* draft-ietf-pppext-eap-ttls-03.txt, Ch. 8.1: * EAP-TTLS Start packet may, in a future specification, be * allowed to contain data. Client based on this draft version * must ignore such data but must not reject the Start packet. */ left = 0; } else if (!data->ssl_initialized) { wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not " "include Start flag"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; return NULL; } resp = NULL; if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && !data->resuming) { res = eap_ttls_decrypt(sm, data, ret, req, pos, left, &resp, respDataLen); } else { res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, id, pos, left, &resp, respDataLen); if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to Phase 2"); if (data->resuming) { wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth -" " may skip Phase 2"); ret->decision = DECISION_COND_SUCC; ret->methodState = METHOD_MAY_CONT; } data->phase2_start = 1; if (data->ttls_version == 0) eap_ttls_v0_derive_key(sm, data); if (*respDataLen == 0) { if (eap_ttls_decrypt(sm, data, ret, req, NULL, 0, &resp, respDataLen)) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to process early " "start for Phase 2"); } res = 0; } data->resuming = 0; } if (res == 2) { /* * Application data included in the handshake message. */ os_free(data->pending_phase2_req); data->pending_phase2_req = resp; data->pending_phase2_req_len = *respDataLen; resp = NULL; *respDataLen = 0; res = eap_ttls_decrypt(sm, data, ret, req, pos, left, &resp, respDataLen); } } if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; if (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully"); data->phase2_success = 1; } } else if (data->ttls_version == 0 && sm->workaround && ret->methodState == METHOD_MAY_CONT && (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully (EAP workaround)"); data->phase2_success = 1; } if (res == 1) { return eap_tls_build_ack(&data->ssl, respDataLen, id, EAP_TYPE_TTLS, data->ttls_version); } return resp; } static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && data->phase2_success; } static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; os_free(data->pending_phase2_req); data->pending_phase2_req = NULL; } 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; if (eap_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; } if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); data->phase2_start = 0; data->phase2_success = 0; data->resuming = 1; data->reauth = 1; return priv; } static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, size_t buflen, int verbose) { struct eap_ttls_data *data = priv; int len, ret; len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose); ret = os_snprintf(buf + len, buflen - len, "EAP-TTLSv%d Phase2 method=", data->ttls_version); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; switch (data->phase2_type) { case EAP_TTLS_PHASE2_EAP: ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", data->phase2_method ? data->phase2_method->name : "?"); break; case EAP_TTLS_PHASE2_MSCHAPV2: ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); break; case EAP_TTLS_PHASE2_MSCHAP: ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); break; case EAP_TTLS_PHASE2_PAP: ret = os_snprintf(buf + len, buflen - len, "PAP\n"); break; case EAP_TTLS_PHASE2_CHAP: ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); break; default: ret = 0; break; } if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; return len; } static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; return data->key_data != NULL && data->phase2_success; } static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; u8 *key; if (data->key_data == NULL || !data->phase2_success) return NULL; key = os_malloc(EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } int eap_peer_ttls_register(void) { struct eap_method *eap; int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); if (eap == NULL) return -1; eap->init = eap_ttls_init; eap->deinit = eap_ttls_deinit; eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; 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; eap->init_for_reauth = eap_ttls_init_for_reauth; ret = eap_peer_method_register(eap); if (ret) eap_peer_method_free(eap); return ret; }