eap_ttls.c revision 214734
1189251Ssam/* 2189251Ssam * EAP peer method: EAP-TTLS (RFC 5281) 3189251Ssam * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18214734Srpaulo#include "crypto/ms_funcs.h" 19214734Srpaulo#include "crypto/sha1.h" 20214734Srpaulo#include "crypto/tls.h" 21189251Ssam#include "eap_common/chap.h" 22214734Srpaulo#include "eap_common/eap_ttls.h" 23189251Ssam#include "mschapv2.h" 24214734Srpaulo#include "eap_i.h" 25214734Srpaulo#include "eap_tls_common.h" 26214734Srpaulo#include "eap_config.h" 27189251Ssam 28189251Ssam 29189251Ssam/* Maximum supported TTLS version 30189251Ssam * 0 = RFC 5281 31189251Ssam * 1 = draft-funk-eap-ttls-v1-00.txt 32189251Ssam */ 33189251Ssam#ifndef EAP_TTLS_VERSION 34189251Ssam#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ 35189251Ssam#endif /* EAP_TTLS_VERSION */ 36189251Ssam 37189251Ssam 38189251Ssam#define MSCHAPV2_KEY_LEN 16 39189251Ssam#define MSCHAPV2_NT_RESPONSE_LEN 24 40189251Ssam 41189251Ssam 42189251Ssamstatic void eap_ttls_deinit(struct eap_sm *sm, void *priv); 43189251Ssam 44189251Ssam 45189251Ssamstruct eap_ttls_data { 46189251Ssam struct eap_ssl_data ssl; 47189251Ssam int ssl_initialized; 48189251Ssam 49189251Ssam int ttls_version, force_ttls_version; 50189251Ssam 51189251Ssam const struct eap_method *phase2_method; 52189251Ssam void *phase2_priv; 53189251Ssam int phase2_success; 54189251Ssam int phase2_start; 55189251Ssam 56189251Ssam enum phase2_types { 57189251Ssam EAP_TTLS_PHASE2_EAP, 58189251Ssam EAP_TTLS_PHASE2_MSCHAPV2, 59189251Ssam EAP_TTLS_PHASE2_MSCHAP, 60189251Ssam EAP_TTLS_PHASE2_PAP, 61189251Ssam EAP_TTLS_PHASE2_CHAP 62189251Ssam } phase2_type; 63189251Ssam struct eap_method_type phase2_eap_type; 64189251Ssam struct eap_method_type *phase2_eap_types; 65189251Ssam size_t num_phase2_eap_types; 66189251Ssam 67189251Ssam u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; 68189251Ssam int auth_response_valid; 69189251Ssam u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ 70189251Ssam u8 ident; 71189251Ssam int resuming; /* starting a resumed session */ 72189251Ssam int reauth; /* reauthentication */ 73189251Ssam u8 *key_data; 74189251Ssam 75189251Ssam struct wpabuf *pending_phase2_req; 76189251Ssam 77189251Ssam#ifdef EAP_TNC 78189251Ssam int ready_for_tnc; 79189251Ssam int tnc_started; 80189251Ssam#endif /* EAP_TNC */ 81189251Ssam}; 82189251Ssam 83189251Ssam 84189251Ssamstatic void * eap_ttls_init(struct eap_sm *sm) 85189251Ssam{ 86189251Ssam struct eap_ttls_data *data; 87189251Ssam struct eap_peer_config *config = eap_get_config(sm); 88189251Ssam char *selected; 89189251Ssam 90189251Ssam data = os_zalloc(sizeof(*data)); 91189251Ssam if (data == NULL) 92189251Ssam return NULL; 93189251Ssam data->ttls_version = EAP_TTLS_VERSION; 94189251Ssam data->force_ttls_version = -1; 95189251Ssam selected = "EAP"; 96189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 97189251Ssam 98189251Ssam#if EAP_TTLS_VERSION > 0 99189251Ssam if (config && config->phase1) { 100189251Ssam const char *pos = os_strstr(config->phase1, "ttlsver="); 101189251Ssam if (pos) { 102189251Ssam data->force_ttls_version = atoi(pos + 8); 103189251Ssam data->ttls_version = data->force_ttls_version; 104189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version " 105189251Ssam "%d", data->force_ttls_version); 106189251Ssam } 107189251Ssam } 108189251Ssam#endif /* EAP_TTLS_VERSION */ 109189251Ssam 110189251Ssam if (config && config->phase2) { 111189251Ssam if (os_strstr(config->phase2, "autheap=")) { 112189251Ssam selected = "EAP"; 113189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 114189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { 115189251Ssam selected = "MSCHAPV2"; 116189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; 117189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAP")) { 118189251Ssam selected = "MSCHAP"; 119189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; 120189251Ssam } else if (os_strstr(config->phase2, "auth=PAP")) { 121189251Ssam selected = "PAP"; 122189251Ssam data->phase2_type = EAP_TTLS_PHASE2_PAP; 123189251Ssam } else if (os_strstr(config->phase2, "auth=CHAP")) { 124189251Ssam selected = "CHAP"; 125189251Ssam data->phase2_type = EAP_TTLS_PHASE2_CHAP; 126189251Ssam } 127189251Ssam } 128189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); 129189251Ssam 130189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { 131189251Ssam if (eap_peer_select_phase2_methods(config, "autheap=", 132189251Ssam &data->phase2_eap_types, 133189251Ssam &data->num_phase2_eap_types) 134189251Ssam < 0) { 135189251Ssam eap_ttls_deinit(sm, data); 136189251Ssam return NULL; 137189251Ssam } 138189251Ssam 139189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 140189251Ssam data->phase2_eap_type.method = EAP_TYPE_NONE; 141189251Ssam } 142189251Ssam 143189251Ssam#if EAP_TTLS_VERSION > 0 144189251Ssam if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && 145189251Ssam data->ttls_version > 0) { 146189251Ssam if (data->force_ttls_version > 0) { 147189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " 148189251Ssam "TLS library does not support TLS/IA.", 149189251Ssam data->force_ttls_version); 150189251Ssam eap_ttls_deinit(sm, data); 151189251Ssam return NULL; 152189251Ssam } 153189251Ssam data->ttls_version = 0; 154189251Ssam } 155189251Ssam#endif /* EAP_TTLS_VERSION */ 156189251Ssam 157189251Ssam return data; 158189251Ssam} 159189251Ssam 160189251Ssam 161189251Ssamstatic void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, 162189251Ssam struct eap_ttls_data *data) 163189251Ssam{ 164189251Ssam if (data->phase2_priv && data->phase2_method) { 165189251Ssam data->phase2_method->deinit(sm, data->phase2_priv); 166189251Ssam data->phase2_method = NULL; 167189251Ssam data->phase2_priv = NULL; 168189251Ssam } 169189251Ssam} 170189251Ssam 171189251Ssam 172189251Ssamstatic void eap_ttls_deinit(struct eap_sm *sm, void *priv) 173189251Ssam{ 174189251Ssam struct eap_ttls_data *data = priv; 175189251Ssam if (data == NULL) 176189251Ssam return; 177189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 178189251Ssam os_free(data->phase2_eap_types); 179189251Ssam if (data->ssl_initialized) 180189251Ssam eap_peer_tls_ssl_deinit(sm, &data->ssl); 181189251Ssam os_free(data->key_data); 182189251Ssam wpabuf_free(data->pending_phase2_req); 183189251Ssam os_free(data); 184189251Ssam} 185189251Ssam 186189251Ssam 187189251Ssamstatic u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, 188189251Ssam int mandatory, size_t len) 189189251Ssam{ 190189251Ssam struct ttls_avp_vendor *avp; 191189251Ssam u8 flags; 192189251Ssam size_t hdrlen; 193189251Ssam 194189251Ssam avp = (struct ttls_avp_vendor *) avphdr; 195189251Ssam flags = mandatory ? AVP_FLAGS_MANDATORY : 0; 196189251Ssam if (vendor_id) { 197189251Ssam flags |= AVP_FLAGS_VENDOR; 198189251Ssam hdrlen = sizeof(*avp); 199189251Ssam avp->vendor_id = host_to_be32(vendor_id); 200189251Ssam } else { 201189251Ssam hdrlen = sizeof(struct ttls_avp); 202189251Ssam } 203189251Ssam 204189251Ssam avp->avp_code = host_to_be32(avp_code); 205189251Ssam avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); 206189251Ssam 207189251Ssam return avphdr + hdrlen; 208189251Ssam} 209189251Ssam 210189251Ssam 211189251Ssamstatic u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, 212189251Ssam u32 vendor_id, int mandatory, 213189251Ssam const u8 *data, size_t len) 214189251Ssam{ 215189251Ssam u8 *pos; 216189251Ssam pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); 217189251Ssam os_memcpy(pos, data, len); 218189251Ssam pos += len; 219189251Ssam AVP_PAD(start, pos); 220189251Ssam return pos; 221189251Ssam} 222189251Ssam 223189251Ssam 224189251Ssamstatic int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, 225189251Ssam int mandatory) 226189251Ssam{ 227189251Ssam struct wpabuf *msg; 228189251Ssam u8 *avp, *pos; 229189251Ssam 230189251Ssam msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); 231189251Ssam if (msg == NULL) { 232189251Ssam wpabuf_free(*resp); 233189251Ssam *resp = NULL; 234189251Ssam return -1; 235189251Ssam } 236189251Ssam 237189251Ssam avp = wpabuf_mhead(msg); 238189251Ssam pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); 239189251Ssam os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); 240189251Ssam pos += wpabuf_len(*resp); 241189251Ssam AVP_PAD(avp, pos); 242189251Ssam wpabuf_free(*resp); 243189251Ssam wpabuf_put(msg, pos - avp); 244189251Ssam *resp = msg; 245189251Ssam return 0; 246189251Ssam} 247189251Ssam 248189251Ssam 249189251Ssam#if EAP_TTLS_VERSION > 0 250189251Ssamstatic int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, 251189251Ssam struct eap_ttls_data *data, 252189251Ssam const u8 *key, size_t key_len) 253189251Ssam{ 254189251Ssam u8 *buf; 255189251Ssam size_t buf_len; 256189251Ssam int ret; 257189251Ssam 258189251Ssam if (key) { 259189251Ssam buf_len = 2 + key_len; 260189251Ssam buf = os_malloc(buf_len); 261189251Ssam if (buf == NULL) 262189251Ssam return -1; 263189251Ssam WPA_PUT_BE16(buf, key_len); 264189251Ssam os_memcpy(buf + 2, key, key_len); 265189251Ssam } else { 266189251Ssam buf = NULL; 267189251Ssam buf_len = 0; 268189251Ssam } 269189251Ssam 270189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " 271189251Ssam "secret permutation", buf, buf_len); 272189251Ssam ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, 273189251Ssam data->ssl.conn, 274189251Ssam buf, buf_len); 275189251Ssam os_free(buf); 276189251Ssam 277189251Ssam return ret; 278189251Ssam} 279189251Ssam#endif /* EAP_TTLS_VERSION */ 280189251Ssam 281189251Ssam 282189251Ssamstatic int eap_ttls_v0_derive_key(struct eap_sm *sm, 283189251Ssam struct eap_ttls_data *data) 284189251Ssam{ 285189251Ssam os_free(data->key_data); 286189251Ssam data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, 287189251Ssam "ttls keying material", 288189251Ssam EAP_TLS_KEY_LEN); 289189251Ssam if (!data->key_data) { 290189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); 291189251Ssam return -1; 292189251Ssam } 293189251Ssam 294189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", 295189251Ssam data->key_data, EAP_TLS_KEY_LEN); 296189251Ssam 297189251Ssam return 0; 298189251Ssam} 299189251Ssam 300189251Ssam 301189251Ssam#if EAP_TTLS_VERSION > 0 302189251Ssamstatic int eap_ttls_v1_derive_key(struct eap_sm *sm, 303189251Ssam struct eap_ttls_data *data) 304189251Ssam{ 305189251Ssam struct tls_keys keys; 306189251Ssam u8 *rnd; 307189251Ssam 308189251Ssam os_free(data->key_data); 309189251Ssam data->key_data = NULL; 310189251Ssam 311189251Ssam os_memset(&keys, 0, sizeof(keys)); 312189251Ssam if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || 313189251Ssam keys.client_random == NULL || keys.server_random == NULL || 314189251Ssam keys.inner_secret == NULL) { 315189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " 316189251Ssam "client random, or server random to derive keying " 317189251Ssam "material"); 318189251Ssam return -1; 319189251Ssam } 320189251Ssam 321189251Ssam rnd = os_malloc(keys.client_random_len + keys.server_random_len); 322189251Ssam data->key_data = os_malloc(EAP_TLS_KEY_LEN); 323189251Ssam if (rnd == NULL || data->key_data == NULL) { 324189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); 325189251Ssam os_free(rnd); 326189251Ssam os_free(data->key_data); 327189251Ssam data->key_data = NULL; 328189251Ssam return -1; 329189251Ssam } 330189251Ssam os_memcpy(rnd, keys.client_random, keys.client_random_len); 331189251Ssam os_memcpy(rnd + keys.client_random_len, keys.server_random, 332189251Ssam keys.server_random_len); 333189251Ssam 334189251Ssam if (tls_prf(keys.inner_secret, keys.inner_secret_len, 335189251Ssam "ttls v1 keying material", rnd, keys.client_random_len + 336189251Ssam keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) { 337189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); 338189251Ssam os_free(rnd); 339189251Ssam os_free(data->key_data); 340189251Ssam data->key_data = NULL; 341189251Ssam return -1; 342189251Ssam } 343189251Ssam 344189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", 345189251Ssam rnd, keys.client_random_len + keys.server_random_len); 346189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", 347189251Ssam keys.inner_secret, keys.inner_secret_len); 348189251Ssam 349189251Ssam os_free(rnd); 350189251Ssam 351189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", 352189251Ssam data->key_data, EAP_TLS_KEY_LEN); 353189251Ssam 354189251Ssam return 0; 355189251Ssam} 356189251Ssam#endif /* EAP_TTLS_VERSION */ 357189251Ssam 358189251Ssam 359189251Ssamstatic u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, 360189251Ssam struct eap_ttls_data *data, size_t len) 361189251Ssam{ 362189251Ssam#if EAP_TTLS_VERSION > 0 363189251Ssam struct tls_keys keys; 364189251Ssam u8 *challenge, *rnd; 365189251Ssam#endif /* EAP_TTLS_VERSION */ 366189251Ssam 367189251Ssam if (data->ttls_version == 0) { 368189251Ssam return eap_peer_tls_derive_key(sm, &data->ssl, 369189251Ssam "ttls challenge", len); 370189251Ssam } 371189251Ssam 372189251Ssam#if EAP_TTLS_VERSION > 0 373189251Ssam 374189251Ssam os_memset(&keys, 0, sizeof(keys)); 375189251Ssam if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || 376189251Ssam keys.client_random == NULL || keys.server_random == NULL || 377189251Ssam keys.inner_secret == NULL) { 378189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " 379189251Ssam "client random, or server random to derive " 380189251Ssam "implicit challenge"); 381189251Ssam return NULL; 382189251Ssam } 383189251Ssam 384189251Ssam rnd = os_malloc(keys.client_random_len + keys.server_random_len); 385189251Ssam challenge = os_malloc(len); 386189251Ssam if (rnd == NULL || challenge == NULL) { 387189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " 388189251Ssam "challenge derivation"); 389189251Ssam os_free(rnd); 390189251Ssam os_free(challenge); 391189251Ssam return NULL; 392189251Ssam } 393189251Ssam os_memcpy(rnd, keys.server_random, keys.server_random_len); 394189251Ssam os_memcpy(rnd + keys.server_random_len, keys.client_random, 395189251Ssam keys.client_random_len); 396189251Ssam 397189251Ssam if (tls_prf(keys.inner_secret, keys.inner_secret_len, 398189251Ssam "inner application challenge", rnd, 399189251Ssam keys.client_random_len + keys.server_random_len, 400189251Ssam challenge, len)) { 401189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " 402189251Ssam "challenge"); 403189251Ssam os_free(rnd); 404189251Ssam os_free(challenge); 405189251Ssam return NULL; 406189251Ssam } 407189251Ssam 408189251Ssam os_free(rnd); 409189251Ssam 410189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", 411189251Ssam challenge, len); 412189251Ssam 413189251Ssam return challenge; 414189251Ssam 415189251Ssam#else /* EAP_TTLS_VERSION */ 416189251Ssam 417189251Ssam return NULL; 418189251Ssam 419189251Ssam#endif /* EAP_TTLS_VERSION */ 420189251Ssam} 421189251Ssam 422189251Ssam 423189251Ssamstatic void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm, 424189251Ssam struct eap_ttls_data *data, 425189251Ssam struct eap_method_ret *ret) 426189251Ssam{ 427189251Ssam#if EAP_TTLS_VERSION > 0 428189251Ssam if (data->ttls_version > 0) { 429189251Ssam const struct eap_method *m = data->phase2_method; 430189251Ssam void *priv = data->phase2_priv; 431189251Ssam 432189251Ssam /* TTLSv1 requires TLS/IA FinalPhaseFinished */ 433189251Ssam if (ret->decision == DECISION_UNCOND_SUCC) 434189251Ssam ret->decision = DECISION_COND_SUCC; 435189251Ssam ret->methodState = METHOD_CONT; 436189251Ssam 437189251Ssam if (ret->decision == DECISION_COND_SUCC && 438189251Ssam m->isKeyAvailable && m->getKey && 439189251Ssam m->isKeyAvailable(sm, priv)) { 440189251Ssam u8 *key; 441189251Ssam size_t key_len; 442189251Ssam key = m->getKey(sm, priv, &key_len); 443189251Ssam if (key) { 444189251Ssam eap_ttls_ia_permute_inner_secret( 445189251Ssam sm, data, key, key_len); 446189251Ssam os_free(key); 447189251Ssam } 448189251Ssam } 449189251Ssam } 450189251Ssam#endif /* EAP_TTLS_VERSION */ 451189251Ssam} 452189251Ssam 453189251Ssam 454189251Ssamstatic void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, 455189251Ssam u8 method) 456189251Ssam{ 457189251Ssam size_t i; 458189251Ssam for (i = 0; i < data->num_phase2_eap_types; i++) { 459189251Ssam if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || 460189251Ssam data->phase2_eap_types[i].method != method) 461189251Ssam continue; 462189251Ssam 463189251Ssam data->phase2_eap_type.vendor = 464189251Ssam data->phase2_eap_types[i].vendor; 465189251Ssam data->phase2_eap_type.method = 466189251Ssam data->phase2_eap_types[i].method; 467189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 468189251Ssam "Phase 2 EAP vendor %d method %d", 469189251Ssam data->phase2_eap_type.vendor, 470189251Ssam data->phase2_eap_type.method); 471189251Ssam break; 472189251Ssam } 473189251Ssam} 474189251Ssam 475189251Ssam 476189251Ssamstatic int eap_ttls_phase2_eap_process(struct eap_sm *sm, 477189251Ssam struct eap_ttls_data *data, 478189251Ssam struct eap_method_ret *ret, 479189251Ssam struct eap_hdr *hdr, size_t len, 480189251Ssam struct wpabuf **resp) 481189251Ssam{ 482189251Ssam struct wpabuf msg; 483189251Ssam struct eap_method_ret iret; 484189251Ssam 485189251Ssam os_memset(&iret, 0, sizeof(iret)); 486189251Ssam wpabuf_set(&msg, hdr, len); 487189251Ssam *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, 488189251Ssam &msg); 489189251Ssam if ((iret.methodState == METHOD_DONE || 490189251Ssam iret.methodState == METHOD_MAY_CONT) && 491189251Ssam (iret.decision == DECISION_UNCOND_SUCC || 492189251Ssam iret.decision == DECISION_COND_SUCC || 493189251Ssam iret.decision == DECISION_FAIL)) { 494189251Ssam ret->methodState = iret.methodState; 495189251Ssam ret->decision = iret.decision; 496189251Ssam } 497189251Ssam eap_ttlsv1_phase2_eap_finish(sm, data, ret); 498189251Ssam 499189251Ssam return 0; 500189251Ssam} 501189251Ssam 502189251Ssam 503189251Ssamstatic int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, 504189251Ssam struct eap_ttls_data *data, 505189251Ssam struct eap_method_ret *ret, 506189251Ssam struct eap_hdr *hdr, size_t len, 507189251Ssam u8 method, struct wpabuf **resp) 508189251Ssam{ 509189251Ssam#ifdef EAP_TNC 510189251Ssam if (data->tnc_started && data->phase2_method && 511189251Ssam data->phase2_priv && method == EAP_TYPE_TNC && 512189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) 513189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, 514189251Ssam resp); 515189251Ssam 516189251Ssam if (data->ready_for_tnc && !data->tnc_started && 517189251Ssam method == EAP_TYPE_TNC) { 518189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 519189251Ssam "EAP method"); 520189251Ssam data->tnc_started = 1; 521189251Ssam } 522189251Ssam 523189251Ssam if (data->tnc_started) { 524189251Ssam if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || 525189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) { 526189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " 527189251Ssam "type %d for TNC", method); 528189251Ssam return -1; 529189251Ssam } 530189251Ssam 531189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 532189251Ssam data->phase2_eap_type.method = method; 533189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 534189251Ssam "Phase 2 EAP vendor %d method %d (TNC)", 535189251Ssam data->phase2_eap_type.vendor, 536189251Ssam data->phase2_eap_type.method); 537189251Ssam 538189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) 539189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 540189251Ssam } 541189251Ssam#endif /* EAP_TNC */ 542189251Ssam 543189251Ssam if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && 544189251Ssam data->phase2_eap_type.method == EAP_TYPE_NONE) 545189251Ssam eap_ttls_phase2_select_eap_method(data, method); 546189251Ssam 547189251Ssam if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) 548189251Ssam { 549189251Ssam if (eap_peer_tls_phase2_nak(data->phase2_eap_types, 550189251Ssam data->num_phase2_eap_types, 551189251Ssam hdr, resp)) 552189251Ssam return -1; 553189251Ssam return 0; 554189251Ssam } 555189251Ssam 556189251Ssam if (data->phase2_priv == NULL) { 557189251Ssam data->phase2_method = eap_peer_get_eap_method( 558189251Ssam EAP_VENDOR_IETF, method); 559189251Ssam if (data->phase2_method) { 560189251Ssam sm->init_phase2 = 1; 561189251Ssam data->phase2_priv = data->phase2_method->init(sm); 562189251Ssam sm->init_phase2 = 0; 563189251Ssam } 564189251Ssam } 565189251Ssam if (data->phase2_priv == NULL || data->phase2_method == NULL) { 566189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " 567189251Ssam "Phase 2 EAP method %d", method); 568189251Ssam return -1; 569189251Ssam } 570189251Ssam 571189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); 572189251Ssam} 573189251Ssam 574189251Ssam 575189251Ssamstatic int eap_ttls_phase2_request_eap(struct eap_sm *sm, 576189251Ssam struct eap_ttls_data *data, 577189251Ssam struct eap_method_ret *ret, 578189251Ssam struct eap_hdr *hdr, 579189251Ssam struct wpabuf **resp) 580189251Ssam{ 581189251Ssam size_t len = be_to_host16(hdr->length); 582189251Ssam u8 *pos; 583189251Ssam struct eap_peer_config *config = eap_get_config(sm); 584189251Ssam 585189251Ssam if (len <= sizeof(struct eap_hdr)) { 586189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: too short " 587189251Ssam "Phase 2 request (len=%lu)", (unsigned long) len); 588189251Ssam return -1; 589189251Ssam } 590189251Ssam pos = (u8 *) (hdr + 1); 591189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); 592189251Ssam switch (*pos) { 593189251Ssam case EAP_TYPE_IDENTITY: 594189251Ssam *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); 595189251Ssam break; 596189251Ssam default: 597189251Ssam if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, 598189251Ssam *pos, resp) < 0) 599189251Ssam return -1; 600189251Ssam break; 601189251Ssam } 602189251Ssam 603189251Ssam if (*resp == NULL && 604189251Ssam (config->pending_req_identity || config->pending_req_password || 605189251Ssam config->pending_req_otp)) { 606189251Ssam return 0; 607189251Ssam } 608189251Ssam 609189251Ssam if (*resp == NULL) 610189251Ssam return -1; 611189251Ssam 612189251Ssam wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", 613189251Ssam *resp); 614189251Ssam return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); 615189251Ssam} 616189251Ssam 617189251Ssam 618189251Ssamstatic void eap_ttlsv1_permute_inner(struct eap_sm *sm, 619189251Ssam struct eap_ttls_data *data) 620189251Ssam{ 621189251Ssam#if EAP_TTLS_VERSION > 0 622189251Ssam u8 session_key[2 * MSCHAPV2_KEY_LEN]; 623189251Ssam 624189251Ssam if (data->ttls_version == 0) 625189251Ssam return; 626189251Ssam 627189251Ssam get_asymetric_start_key(data->master_key, session_key, 628189251Ssam MSCHAPV2_KEY_LEN, 0, 0); 629189251Ssam get_asymetric_start_key(data->master_key, 630189251Ssam session_key + MSCHAPV2_KEY_LEN, 631189251Ssam MSCHAPV2_KEY_LEN, 1, 0); 632189251Ssam eap_ttls_ia_permute_inner_secret(sm, data, session_key, 633189251Ssam sizeof(session_key)); 634189251Ssam#endif /* EAP_TTLS_VERSION */ 635189251Ssam} 636189251Ssam 637189251Ssam 638189251Ssamstatic int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, 639189251Ssam struct eap_ttls_data *data, 640189251Ssam struct eap_method_ret *ret, 641189251Ssam struct wpabuf **resp) 642189251Ssam{ 643189251Ssam struct wpabuf *msg; 644189251Ssam u8 *buf, *pos, *challenge, *peer_challenge; 645189251Ssam const u8 *identity, *password; 646189251Ssam size_t identity_len, password_len; 647189251Ssam int pwhash; 648189251Ssam 649189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); 650189251Ssam 651189251Ssam identity = eap_get_config_identity(sm, &identity_len); 652189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 653189251Ssam if (identity == NULL || password == NULL) 654189251Ssam return -1; 655189251Ssam 656189251Ssam msg = wpabuf_alloc(identity_len + 1000); 657189251Ssam if (msg == NULL) { 658189251Ssam wpa_printf(MSG_ERROR, 659189251Ssam "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); 660189251Ssam return -1; 661189251Ssam } 662189251Ssam pos = buf = wpabuf_mhead(msg); 663189251Ssam 664189251Ssam /* User-Name */ 665189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 666189251Ssam identity, identity_len); 667189251Ssam 668189251Ssam /* MS-CHAP-Challenge */ 669189251Ssam challenge = eap_ttls_implicit_challenge( 670189251Ssam sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); 671189251Ssam if (challenge == NULL) { 672189251Ssam wpabuf_free(msg); 673189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 674189251Ssam "implicit challenge"); 675189251Ssam return -1; 676189251Ssam } 677189251Ssam peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; 678189251Ssam 679189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 680189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 681189251Ssam challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); 682189251Ssam 683189251Ssam /* MS-CHAP2-Response */ 684189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, 685189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 686189251Ssam EAP_TTLS_MSCHAPV2_RESPONSE_LEN); 687189251Ssam data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; 688189251Ssam *pos++ = data->ident; 689189251Ssam *pos++ = 0; /* Flags */ 690189251Ssam os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); 691189251Ssam pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; 692189251Ssam os_memset(pos, 0, 8); /* Reserved, must be zero */ 693189251Ssam pos += 8; 694214734Srpaulo if (mschapv2_derive_response(identity, identity_len, password, 695214734Srpaulo password_len, pwhash, challenge, 696214734Srpaulo peer_challenge, pos, data->auth_response, 697214734Srpaulo data->master_key)) { 698214734Srpaulo wpabuf_free(msg); 699214734Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 700214734Srpaulo "response"); 701214734Srpaulo return -1; 702214734Srpaulo } 703189251Ssam data->auth_response_valid = 1; 704189251Ssam 705189251Ssam eap_ttlsv1_permute_inner(sm, data); 706189251Ssam 707189251Ssam pos += 24; 708189251Ssam os_free(challenge); 709189251Ssam AVP_PAD(buf, pos); 710189251Ssam 711189251Ssam wpabuf_put(msg, pos - buf); 712189251Ssam *resp = msg; 713189251Ssam 714189251Ssam if (sm->workaround && data->ttls_version == 0) { 715189251Ssam /* At least FreeRADIUS seems to be terminating 716189251Ssam * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success 717189251Ssam * packet. */ 718189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " 719189251Ssam "allow success without tunneled response"); 720189251Ssam ret->methodState = METHOD_MAY_CONT; 721189251Ssam ret->decision = DECISION_COND_SUCC; 722189251Ssam } 723189251Ssam 724189251Ssam return 0; 725189251Ssam} 726189251Ssam 727189251Ssam 728189251Ssamstatic int eap_ttls_phase2_request_mschap(struct eap_sm *sm, 729189251Ssam struct eap_ttls_data *data, 730189251Ssam struct eap_method_ret *ret, 731189251Ssam struct wpabuf **resp) 732189251Ssam{ 733189251Ssam struct wpabuf *msg; 734189251Ssam u8 *buf, *pos, *challenge; 735189251Ssam const u8 *identity, *password; 736189251Ssam size_t identity_len, password_len; 737189251Ssam int pwhash; 738189251Ssam 739189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); 740189251Ssam 741189251Ssam identity = eap_get_config_identity(sm, &identity_len); 742189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 743189251Ssam if (identity == NULL || password == NULL) 744189251Ssam return -1; 745189251Ssam 746189251Ssam msg = wpabuf_alloc(identity_len + 1000); 747189251Ssam if (msg == NULL) { 748189251Ssam wpa_printf(MSG_ERROR, 749189251Ssam "EAP-TTLS/MSCHAP: Failed to allocate memory"); 750189251Ssam return -1; 751189251Ssam } 752189251Ssam pos = buf = wpabuf_mhead(msg); 753189251Ssam 754189251Ssam /* User-Name */ 755189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 756189251Ssam identity, identity_len); 757189251Ssam 758189251Ssam /* MS-CHAP-Challenge */ 759189251Ssam challenge = eap_ttls_implicit_challenge( 760189251Ssam sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); 761189251Ssam if (challenge == NULL) { 762189251Ssam wpabuf_free(msg); 763189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " 764189251Ssam "implicit challenge"); 765189251Ssam return -1; 766189251Ssam } 767189251Ssam 768189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 769189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 770189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 771189251Ssam 772189251Ssam /* MS-CHAP-Response */ 773189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, 774189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 775189251Ssam EAP_TTLS_MSCHAP_RESPONSE_LEN); 776189251Ssam data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; 777189251Ssam *pos++ = data->ident; 778189251Ssam *pos++ = 1; /* Flags: Use NT style passwords */ 779189251Ssam os_memset(pos, 0, 24); /* LM-Response */ 780189251Ssam pos += 24; 781189251Ssam if (pwhash) { 782189251Ssam challenge_response(challenge, password, pos); /* NT-Response */ 783189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", 784189251Ssam password, 16); 785189251Ssam } else { 786189251Ssam nt_challenge_response(challenge, password, password_len, 787189251Ssam pos); /* NT-Response */ 788189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", 789189251Ssam password, password_len); 790189251Ssam } 791189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", 792189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 793189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); 794189251Ssam pos += 24; 795189251Ssam os_free(challenge); 796189251Ssam AVP_PAD(buf, pos); 797189251Ssam 798189251Ssam wpabuf_put(msg, pos - buf); 799189251Ssam *resp = msg; 800189251Ssam 801189251Ssam if (data->ttls_version > 0) { 802189251Ssam /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, 803189251Ssam * so do not allow connection to be terminated yet. */ 804189251Ssam ret->methodState = METHOD_CONT; 805189251Ssam ret->decision = DECISION_COND_SUCC; 806189251Ssam } else { 807189251Ssam /* EAP-TTLS/MSCHAP does not provide tunneled success 808189251Ssam * notification, so assume that Phase2 succeeds. */ 809189251Ssam ret->methodState = METHOD_DONE; 810189251Ssam ret->decision = DECISION_COND_SUCC; 811189251Ssam } 812189251Ssam 813189251Ssam return 0; 814189251Ssam} 815189251Ssam 816189251Ssam 817189251Ssamstatic int eap_ttls_phase2_request_pap(struct eap_sm *sm, 818189251Ssam struct eap_ttls_data *data, 819189251Ssam struct eap_method_ret *ret, 820189251Ssam struct wpabuf **resp) 821189251Ssam{ 822189251Ssam struct wpabuf *msg; 823189251Ssam u8 *buf, *pos; 824189251Ssam size_t pad; 825189251Ssam const u8 *identity, *password; 826189251Ssam size_t identity_len, password_len; 827189251Ssam 828189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); 829189251Ssam 830189251Ssam identity = eap_get_config_identity(sm, &identity_len); 831189251Ssam password = eap_get_config_password(sm, &password_len); 832189251Ssam if (identity == NULL || password == NULL) 833189251Ssam return -1; 834189251Ssam 835189251Ssam msg = wpabuf_alloc(identity_len + password_len + 100); 836189251Ssam if (msg == NULL) { 837189251Ssam wpa_printf(MSG_ERROR, 838189251Ssam "EAP-TTLS/PAP: Failed to allocate memory"); 839189251Ssam return -1; 840189251Ssam } 841189251Ssam pos = buf = wpabuf_mhead(msg); 842189251Ssam 843189251Ssam /* User-Name */ 844189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 845189251Ssam identity, identity_len); 846189251Ssam 847189251Ssam /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts 848189251Ssam * the data, so no separate encryption is used in the AVP itself. 849189251Ssam * However, the password is padded to obfuscate its length. */ 850209158Srpaulo pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; 851189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, 852189251Ssam password_len + pad); 853189251Ssam os_memcpy(pos, password, password_len); 854189251Ssam pos += password_len; 855189251Ssam os_memset(pos, 0, pad); 856189251Ssam pos += pad; 857189251Ssam AVP_PAD(buf, pos); 858189251Ssam 859189251Ssam wpabuf_put(msg, pos - buf); 860189251Ssam *resp = msg; 861189251Ssam 862189251Ssam if (data->ttls_version > 0) { 863189251Ssam /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, 864189251Ssam * so do not allow connection to be terminated yet. */ 865189251Ssam ret->methodState = METHOD_CONT; 866189251Ssam ret->decision = DECISION_COND_SUCC; 867189251Ssam } else { 868189251Ssam /* EAP-TTLS/PAP does not provide tunneled success notification, 869189251Ssam * so assume that Phase2 succeeds. */ 870189251Ssam ret->methodState = METHOD_DONE; 871189251Ssam ret->decision = DECISION_COND_SUCC; 872189251Ssam } 873189251Ssam 874189251Ssam return 0; 875189251Ssam} 876189251Ssam 877189251Ssam 878189251Ssamstatic int eap_ttls_phase2_request_chap(struct eap_sm *sm, 879189251Ssam struct eap_ttls_data *data, 880189251Ssam struct eap_method_ret *ret, 881189251Ssam struct wpabuf **resp) 882189251Ssam{ 883189251Ssam struct wpabuf *msg; 884189251Ssam u8 *buf, *pos, *challenge; 885189251Ssam const u8 *identity, *password; 886189251Ssam size_t identity_len, password_len; 887189251Ssam 888189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); 889189251Ssam 890189251Ssam identity = eap_get_config_identity(sm, &identity_len); 891189251Ssam password = eap_get_config_password(sm, &password_len); 892189251Ssam if (identity == NULL || password == NULL) 893189251Ssam return -1; 894189251Ssam 895189251Ssam msg = wpabuf_alloc(identity_len + 1000); 896189251Ssam if (msg == NULL) { 897189251Ssam wpa_printf(MSG_ERROR, 898189251Ssam "EAP-TTLS/CHAP: Failed to allocate memory"); 899189251Ssam return -1; 900189251Ssam } 901189251Ssam pos = buf = wpabuf_mhead(msg); 902189251Ssam 903189251Ssam /* User-Name */ 904189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 905189251Ssam identity, identity_len); 906189251Ssam 907189251Ssam /* CHAP-Challenge */ 908189251Ssam challenge = eap_ttls_implicit_challenge( 909189251Ssam sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); 910189251Ssam if (challenge == NULL) { 911189251Ssam wpabuf_free(msg); 912189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " 913189251Ssam "implicit challenge"); 914189251Ssam return -1; 915189251Ssam } 916189251Ssam 917189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, 918189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 919189251Ssam 920189251Ssam /* CHAP-Password */ 921189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, 922189251Ssam 1 + EAP_TTLS_CHAP_PASSWORD_LEN); 923189251Ssam data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; 924189251Ssam *pos++ = data->ident; 925189251Ssam 926189251Ssam /* MD5(Ident + Password + Challenge) */ 927189251Ssam chap_md5(data->ident, password, password_len, challenge, 928189251Ssam EAP_TTLS_CHAP_CHALLENGE_LEN, pos); 929189251Ssam 930189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", 931189251Ssam identity, identity_len); 932189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", 933189251Ssam password, password_len); 934189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", 935189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 936189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", 937189251Ssam pos, EAP_TTLS_CHAP_PASSWORD_LEN); 938189251Ssam pos += EAP_TTLS_CHAP_PASSWORD_LEN; 939189251Ssam os_free(challenge); 940189251Ssam AVP_PAD(buf, pos); 941189251Ssam 942189251Ssam wpabuf_put(msg, pos - buf); 943189251Ssam *resp = msg; 944189251Ssam 945189251Ssam if (data->ttls_version > 0) { 946189251Ssam /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, 947189251Ssam * so do not allow connection to be terminated yet. */ 948189251Ssam ret->methodState = METHOD_CONT; 949189251Ssam ret->decision = DECISION_COND_SUCC; 950189251Ssam } else { 951189251Ssam /* EAP-TTLS/CHAP does not provide tunneled success 952189251Ssam * notification, so assume that Phase2 succeeds. */ 953189251Ssam ret->methodState = METHOD_DONE; 954189251Ssam ret->decision = DECISION_COND_SUCC; 955189251Ssam } 956189251Ssam 957189251Ssam return 0; 958189251Ssam} 959189251Ssam 960189251Ssam 961189251Ssamstatic int eap_ttls_phase2_request(struct eap_sm *sm, 962189251Ssam struct eap_ttls_data *data, 963189251Ssam struct eap_method_ret *ret, 964189251Ssam struct eap_hdr *hdr, 965189251Ssam struct wpabuf **resp) 966189251Ssam{ 967189251Ssam int res = 0; 968189251Ssam size_t len; 969189251Ssam enum phase2_types phase2_type = data->phase2_type; 970189251Ssam 971189251Ssam#ifdef EAP_TNC 972189251Ssam if (data->tnc_started) { 973189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); 974189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 975189251Ssam } 976189251Ssam#endif /* EAP_TNC */ 977189251Ssam 978189251Ssam if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || 979189251Ssam phase2_type == EAP_TTLS_PHASE2_MSCHAP || 980189251Ssam phase2_type == EAP_TTLS_PHASE2_PAP || 981189251Ssam phase2_type == EAP_TTLS_PHASE2_CHAP) { 982189251Ssam if (eap_get_config_identity(sm, &len) == NULL) { 983189251Ssam wpa_printf(MSG_INFO, 984189251Ssam "EAP-TTLS: Identity not configured"); 985189251Ssam eap_sm_request_identity(sm); 986189251Ssam if (eap_get_config_password(sm, &len) == NULL) 987189251Ssam eap_sm_request_password(sm); 988189251Ssam return 0; 989189251Ssam } 990189251Ssam 991189251Ssam if (eap_get_config_password(sm, &len) == NULL) { 992189251Ssam wpa_printf(MSG_INFO, 993189251Ssam "EAP-TTLS: Password not configured"); 994189251Ssam eap_sm_request_password(sm); 995189251Ssam return 0; 996189251Ssam } 997189251Ssam } 998189251Ssam 999189251Ssam switch (phase2_type) { 1000189251Ssam case EAP_TTLS_PHASE2_EAP: 1001189251Ssam res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); 1002189251Ssam break; 1003189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1004189251Ssam res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); 1005189251Ssam break; 1006189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1007189251Ssam res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); 1008189251Ssam break; 1009189251Ssam case EAP_TTLS_PHASE2_PAP: 1010189251Ssam res = eap_ttls_phase2_request_pap(sm, data, ret, resp); 1011189251Ssam break; 1012189251Ssam case EAP_TTLS_PHASE2_CHAP: 1013189251Ssam res = eap_ttls_phase2_request_chap(sm, data, ret, resp); 1014189251Ssam break; 1015189251Ssam default: 1016189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); 1017189251Ssam res = -1; 1018189251Ssam break; 1019189251Ssam } 1020189251Ssam 1021189251Ssam if (res < 0) { 1022189251Ssam ret->methodState = METHOD_DONE; 1023189251Ssam ret->decision = DECISION_FAIL; 1024189251Ssam } 1025189251Ssam 1026189251Ssam return res; 1027189251Ssam} 1028189251Ssam 1029189251Ssam 1030189251Ssam#if EAP_TTLS_VERSION > 0 1031189251Ssamstatic struct wpabuf * eap_ttls_build_phase_finished( 1032189251Ssam struct eap_sm *sm, struct eap_ttls_data *data, int id, int final) 1033189251Ssam{ 1034214734Srpaulo struct wpabuf *req, *buf; 1035189251Ssam 1036214734Srpaulo buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx, 1037214734Srpaulo data->ssl.conn, 1038214734Srpaulo final); 1039214734Srpaulo if (buf == NULL) 1040214734Srpaulo return NULL; 1041214734Srpaulo 1042214734Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1043214734Srpaulo 1 + wpabuf_len(buf), 1044189251Ssam EAP_CODE_RESPONSE, id); 1045214734Srpaulo if (req == NULL) { 1046214734Srpaulo wpabuf_free(buf); 1047189251Ssam return NULL; 1048214734Srpaulo } 1049189251Ssam 1050189251Ssam wpabuf_put_u8(req, data->ttls_version); 1051214734Srpaulo wpabuf_put_buf(req, buf); 1052214734Srpaulo wpabuf_free(buf); 1053189251Ssam eap_update_len(req); 1054189251Ssam 1055189251Ssam return req; 1056189251Ssam} 1057189251Ssam#endif /* EAP_TTLS_VERSION */ 1058189251Ssam 1059189251Ssam 1060189251Ssamstruct ttls_parse_avp { 1061189251Ssam u8 *mschapv2; 1062189251Ssam u8 *eapdata; 1063189251Ssam size_t eap_len; 1064189251Ssam int mschapv2_error; 1065189251Ssam}; 1066189251Ssam 1067189251Ssam 1068189251Ssamstatic int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, 1069189251Ssam struct ttls_parse_avp *parse) 1070189251Ssam{ 1071189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); 1072189251Ssam if (parse->eapdata == NULL) { 1073189251Ssam parse->eapdata = os_malloc(dlen); 1074189251Ssam if (parse->eapdata == NULL) { 1075189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 1076189251Ssam "memory for Phase 2 EAP data"); 1077189251Ssam return -1; 1078189251Ssam } 1079189251Ssam os_memcpy(parse->eapdata, dpos, dlen); 1080189251Ssam parse->eap_len = dlen; 1081189251Ssam } else { 1082189251Ssam u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); 1083189251Ssam if (neweap == NULL) { 1084189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 1085189251Ssam "memory for Phase 2 EAP data"); 1086189251Ssam return -1; 1087189251Ssam } 1088189251Ssam os_memcpy(neweap + parse->eap_len, dpos, dlen); 1089189251Ssam parse->eapdata = neweap; 1090189251Ssam parse->eap_len += dlen; 1091189251Ssam } 1092189251Ssam 1093189251Ssam return 0; 1094189251Ssam} 1095189251Ssam 1096189251Ssam 1097189251Ssamstatic int eap_ttls_parse_avp(u8 *pos, size_t left, 1098189251Ssam struct ttls_parse_avp *parse) 1099189251Ssam{ 1100189251Ssam struct ttls_avp *avp; 1101189251Ssam u32 avp_code, avp_length, vendor_id = 0; 1102189251Ssam u8 avp_flags, *dpos; 1103189251Ssam size_t dlen; 1104189251Ssam 1105189251Ssam avp = (struct ttls_avp *) pos; 1106189251Ssam avp_code = be_to_host32(avp->avp_code); 1107189251Ssam avp_length = be_to_host32(avp->avp_length); 1108189251Ssam avp_flags = (avp_length >> 24) & 0xff; 1109189251Ssam avp_length &= 0xffffff; 1110189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " 1111189251Ssam "length=%d", (int) avp_code, avp_flags, 1112189251Ssam (int) avp_length); 1113189251Ssam 1114189251Ssam if (avp_length > left) { 1115189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " 1116189251Ssam "(len=%d, left=%lu) - dropped", 1117189251Ssam (int) avp_length, (unsigned long) left); 1118189251Ssam return -1; 1119189251Ssam } 1120189251Ssam 1121189251Ssam if (avp_length < sizeof(*avp)) { 1122189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", 1123189251Ssam avp_length); 1124189251Ssam return -1; 1125189251Ssam } 1126189251Ssam 1127189251Ssam dpos = (u8 *) (avp + 1); 1128189251Ssam dlen = avp_length - sizeof(*avp); 1129189251Ssam if (avp_flags & AVP_FLAGS_VENDOR) { 1130189251Ssam if (dlen < 4) { 1131189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " 1132189251Ssam "underflow"); 1133189251Ssam return -1; 1134189251Ssam } 1135189251Ssam vendor_id = WPA_GET_BE32(dpos); 1136189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", 1137189251Ssam (int) vendor_id); 1138189251Ssam dpos += 4; 1139189251Ssam dlen -= 4; 1140189251Ssam } 1141189251Ssam 1142189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); 1143189251Ssam 1144189251Ssam if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { 1145189251Ssam if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) 1146189251Ssam return -1; 1147189251Ssam } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { 1148189251Ssam /* This is an optional message that can be displayed to 1149189251Ssam * the user. */ 1150189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", 1151189251Ssam dpos, dlen); 1152189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 1153189251Ssam avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { 1154189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", 1155189251Ssam dpos, dlen); 1156189251Ssam if (dlen != 43) { 1157189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " 1158189251Ssam "MS-CHAP2-Success length " 1159189251Ssam "(len=%lu, expected 43)", 1160189251Ssam (unsigned long) dlen); 1161189251Ssam return -1; 1162189251Ssam } 1163189251Ssam parse->mschapv2 = dpos; 1164189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 1165189251Ssam avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { 1166189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", 1167189251Ssam dpos, dlen); 1168189251Ssam parse->mschapv2_error = 1; 1169189251Ssam } else if (avp_flags & AVP_FLAGS_MANDATORY) { 1170189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " 1171189251Ssam "code %d vendor_id %d - dropped", 1172189251Ssam (int) avp_code, (int) vendor_id); 1173189251Ssam return -1; 1174189251Ssam } else { 1175189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " 1176189251Ssam "code %d vendor_id %d", 1177189251Ssam (int) avp_code, (int) vendor_id); 1178189251Ssam } 1179189251Ssam 1180189251Ssam return avp_length; 1181189251Ssam} 1182189251Ssam 1183189251Ssam 1184189251Ssamstatic int eap_ttls_parse_avps(struct wpabuf *in_decrypted, 1185189251Ssam struct ttls_parse_avp *parse) 1186189251Ssam{ 1187189251Ssam u8 *pos; 1188189251Ssam size_t left, pad; 1189189251Ssam int avp_length; 1190189251Ssam 1191189251Ssam pos = wpabuf_mhead(in_decrypted); 1192189251Ssam left = wpabuf_len(in_decrypted); 1193189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); 1194189251Ssam if (left < sizeof(struct ttls_avp)) { 1195189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" 1196189251Ssam " len=%lu expected %lu or more - dropped", 1197189251Ssam (unsigned long) left, 1198189251Ssam (unsigned long) sizeof(struct ttls_avp)); 1199189251Ssam return -1; 1200189251Ssam } 1201189251Ssam 1202189251Ssam /* Parse AVPs */ 1203189251Ssam os_memset(parse, 0, sizeof(*parse)); 1204189251Ssam 1205189251Ssam while (left > 0) { 1206189251Ssam avp_length = eap_ttls_parse_avp(pos, left, parse); 1207189251Ssam if (avp_length < 0) 1208189251Ssam return -1; 1209189251Ssam 1210189251Ssam pad = (4 - (avp_length & 3)) & 3; 1211189251Ssam pos += avp_length + pad; 1212189251Ssam if (left < avp_length + pad) 1213189251Ssam left = 0; 1214189251Ssam else 1215189251Ssam left -= avp_length + pad; 1216189251Ssam } 1217189251Ssam 1218189251Ssam return 0; 1219189251Ssam} 1220189251Ssam 1221189251Ssam 1222189251Ssamstatic u8 * eap_ttls_fake_identity_request(void) 1223189251Ssam{ 1224189251Ssam struct eap_hdr *hdr; 1225189251Ssam u8 *buf; 1226189251Ssam 1227189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " 1228189251Ssam "Phase 2 - use fake EAP-Request Identity"); 1229189251Ssam buf = os_malloc(sizeof(*hdr) + 1); 1230189251Ssam if (buf == NULL) { 1231189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " 1232189251Ssam "memory for fake EAP-Identity Request"); 1233189251Ssam return NULL; 1234189251Ssam } 1235189251Ssam 1236189251Ssam hdr = (struct eap_hdr *) buf; 1237189251Ssam hdr->code = EAP_CODE_REQUEST; 1238189251Ssam hdr->identifier = 0; 1239189251Ssam hdr->length = host_to_be16(sizeof(*hdr) + 1); 1240189251Ssam buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; 1241189251Ssam 1242189251Ssam return buf; 1243189251Ssam} 1244189251Ssam 1245189251Ssam 1246189251Ssamstatic int eap_ttls_encrypt_response(struct eap_sm *sm, 1247189251Ssam struct eap_ttls_data *data, 1248189251Ssam struct wpabuf *resp, u8 identifier, 1249189251Ssam struct wpabuf **out_data) 1250189251Ssam{ 1251189251Ssam if (resp == NULL) 1252189251Ssam return 0; 1253189251Ssam 1254189251Ssam wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", 1255189251Ssam resp); 1256189251Ssam if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 1257189251Ssam data->ttls_version, identifier, 1258189251Ssam resp, out_data)) { 1259189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " 1260189251Ssam "frame"); 1261189251Ssam return -1; 1262189251Ssam } 1263189251Ssam wpabuf_free(resp); 1264189251Ssam 1265189251Ssam return 0; 1266189251Ssam} 1267189251Ssam 1268189251Ssam 1269189251Ssamstatic int eap_ttls_process_phase2_eap(struct eap_sm *sm, 1270189251Ssam struct eap_ttls_data *data, 1271189251Ssam struct eap_method_ret *ret, 1272189251Ssam struct ttls_parse_avp *parse, 1273189251Ssam struct wpabuf **resp) 1274189251Ssam{ 1275189251Ssam struct eap_hdr *hdr; 1276189251Ssam size_t len; 1277189251Ssam 1278189251Ssam if (parse->eapdata == NULL) { 1279189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " 1280189251Ssam "packet - dropped"); 1281189251Ssam return -1; 1282189251Ssam } 1283189251Ssam 1284189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", 1285189251Ssam parse->eapdata, parse->eap_len); 1286189251Ssam hdr = (struct eap_hdr *) parse->eapdata; 1287189251Ssam 1288189251Ssam if (parse->eap_len < sizeof(*hdr)) { 1289189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " 1290189251Ssam "frame (len=%lu, expected %lu or more) - dropped", 1291189251Ssam (unsigned long) parse->eap_len, 1292189251Ssam (unsigned long) sizeof(*hdr)); 1293189251Ssam return -1; 1294189251Ssam } 1295189251Ssam len = be_to_host16(hdr->length); 1296189251Ssam if (len > parse->eap_len) { 1297189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " 1298189251Ssam "EAP frame (EAP hdr len=%lu, EAP data len in " 1299189251Ssam "AVP=%lu)", 1300189251Ssam (unsigned long) len, 1301189251Ssam (unsigned long) parse->eap_len); 1302189251Ssam return -1; 1303189251Ssam } 1304189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " 1305189251Ssam "identifier=%d length=%lu", 1306189251Ssam hdr->code, hdr->identifier, (unsigned long) len); 1307189251Ssam switch (hdr->code) { 1308189251Ssam case EAP_CODE_REQUEST: 1309189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { 1310189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1311189251Ssam "processing failed"); 1312189251Ssam return -1; 1313189251Ssam } 1314189251Ssam break; 1315189251Ssam default: 1316189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " 1317189251Ssam "Phase 2 EAP header", hdr->code); 1318189251Ssam return -1; 1319189251Ssam } 1320189251Ssam 1321189251Ssam return 0; 1322189251Ssam} 1323189251Ssam 1324189251Ssam 1325189251Ssamstatic int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, 1326189251Ssam struct eap_ttls_data *data, 1327189251Ssam struct eap_method_ret *ret, 1328189251Ssam struct ttls_parse_avp *parse) 1329189251Ssam{ 1330189251Ssam if (parse->mschapv2_error) { 1331189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " 1332189251Ssam "MS-CHAP-Error - failed"); 1333189251Ssam ret->methodState = METHOD_DONE; 1334189251Ssam ret->decision = DECISION_FAIL; 1335189251Ssam /* Reply with empty data to ACK error */ 1336189251Ssam return 1; 1337189251Ssam } 1338189251Ssam 1339189251Ssam if (parse->mschapv2 == NULL) { 1340189251Ssam#ifdef EAP_TNC 1341189251Ssam if (data->phase2_success && parse->eapdata) { 1342189251Ssam /* 1343189251Ssam * Allow EAP-TNC to be started after successfully 1344189251Ssam * completed MSCHAPV2. 1345189251Ssam */ 1346189251Ssam return 1; 1347189251Ssam } 1348189251Ssam#endif /* EAP_TNC */ 1349189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " 1350189251Ssam "received for Phase2 MSCHAPV2"); 1351189251Ssam return -1; 1352189251Ssam } 1353189251Ssam if (parse->mschapv2[0] != data->ident) { 1354189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " 1355189251Ssam "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", 1356189251Ssam parse->mschapv2[0], data->ident); 1357189251Ssam return -1; 1358189251Ssam } 1359189251Ssam if (!data->auth_response_valid || 1360189251Ssam mschapv2_verify_auth_response(data->auth_response, 1361189251Ssam parse->mschapv2 + 1, 42)) { 1362189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " 1363189251Ssam "response in Phase 2 MSCHAPV2 success request"); 1364189251Ssam return -1; 1365189251Ssam } 1366189251Ssam 1367189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " 1368189251Ssam "authentication succeeded"); 1369189251Ssam if (data->ttls_version > 0) { 1370189251Ssam /* 1371189251Ssam * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report 1372189251Ssam * success, so do not allow connection to be terminated 1373189251Ssam * yet. 1374189251Ssam */ 1375189251Ssam ret->methodState = METHOD_CONT; 1376189251Ssam ret->decision = DECISION_COND_SUCC; 1377189251Ssam } else { 1378189251Ssam ret->methodState = METHOD_DONE; 1379189251Ssam ret->decision = DECISION_UNCOND_SUCC; 1380189251Ssam data->phase2_success = 1; 1381189251Ssam } 1382189251Ssam 1383189251Ssam /* 1384189251Ssam * Reply with empty data; authentication server will reply 1385189251Ssam * with EAP-Success after this. 1386189251Ssam */ 1387189251Ssam return 1; 1388189251Ssam} 1389189251Ssam 1390189251Ssam 1391189251Ssam#ifdef EAP_TNC 1392189251Ssamstatic int eap_ttls_process_tnc_start(struct eap_sm *sm, 1393189251Ssam struct eap_ttls_data *data, 1394189251Ssam struct eap_method_ret *ret, 1395189251Ssam struct ttls_parse_avp *parse, 1396189251Ssam struct wpabuf **resp) 1397189251Ssam{ 1398189251Ssam /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ 1399189251Ssam if (parse->eapdata == NULL) { 1400189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1401189251Ssam "unexpected tunneled data (no EAP)"); 1402189251Ssam return -1; 1403189251Ssam } 1404189251Ssam 1405189251Ssam if (!data->ready_for_tnc) { 1406189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1407189251Ssam "EAP after non-EAP, but not ready for TNC"); 1408189251Ssam return -1; 1409189251Ssam } 1410189251Ssam 1411189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 1412189251Ssam "non-EAP method"); 1413189251Ssam data->tnc_started = 1; 1414189251Ssam 1415189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) 1416189251Ssam return -1; 1417189251Ssam 1418189251Ssam return 0; 1419189251Ssam} 1420189251Ssam#endif /* EAP_TNC */ 1421189251Ssam 1422189251Ssam 1423189251Ssamstatic int eap_ttls_process_decrypted(struct eap_sm *sm, 1424189251Ssam struct eap_ttls_data *data, 1425189251Ssam struct eap_method_ret *ret, 1426189251Ssam u8 identifier, 1427189251Ssam struct ttls_parse_avp *parse, 1428189251Ssam struct wpabuf *in_decrypted, 1429189251Ssam struct wpabuf **out_data) 1430189251Ssam{ 1431189251Ssam struct wpabuf *resp = NULL; 1432189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1433189251Ssam int res; 1434189251Ssam enum phase2_types phase2_type = data->phase2_type; 1435189251Ssam 1436189251Ssam#ifdef EAP_TNC 1437189251Ssam if (data->tnc_started) 1438189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 1439189251Ssam#endif /* EAP_TNC */ 1440189251Ssam 1441189251Ssam switch (phase2_type) { 1442189251Ssam case EAP_TTLS_PHASE2_EAP: 1443189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < 1444189251Ssam 0) 1445189251Ssam return -1; 1446189251Ssam break; 1447189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1448189251Ssam res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); 1449189251Ssam#ifdef EAP_TNC 1450189251Ssam if (res == 1 && parse->eapdata && data->phase2_success) { 1451189251Ssam /* 1452189251Ssam * TNC may be required as the next 1453189251Ssam * authentication method within the tunnel. 1454189251Ssam */ 1455189251Ssam ret->methodState = METHOD_MAY_CONT; 1456189251Ssam data->ready_for_tnc = 1; 1457189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, 1458189251Ssam &resp) == 0) 1459189251Ssam break; 1460189251Ssam } 1461189251Ssam#endif /* EAP_TNC */ 1462189251Ssam return res; 1463189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1464189251Ssam case EAP_TTLS_PHASE2_PAP: 1465189251Ssam case EAP_TTLS_PHASE2_CHAP: 1466189251Ssam#ifdef EAP_TNC 1467189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < 1468189251Ssam 0) 1469189251Ssam return -1; 1470189251Ssam break; 1471189251Ssam#else /* EAP_TNC */ 1472189251Ssam /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled 1473189251Ssam * requests to the supplicant */ 1474189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " 1475189251Ssam "tunneled data"); 1476189251Ssam return -1; 1477189251Ssam#endif /* EAP_TNC */ 1478189251Ssam } 1479189251Ssam 1480189251Ssam if (resp) { 1481189251Ssam if (eap_ttls_encrypt_response(sm, data, resp, identifier, 1482189251Ssam out_data) < 0) 1483189251Ssam return -1; 1484189251Ssam } else if (config->pending_req_identity || 1485189251Ssam config->pending_req_password || 1486189251Ssam config->pending_req_otp || 1487189251Ssam config->pending_req_new_password) { 1488189251Ssam wpabuf_free(data->pending_phase2_req); 1489189251Ssam data->pending_phase2_req = wpabuf_dup(in_decrypted); 1490189251Ssam } 1491189251Ssam 1492189251Ssam return 0; 1493189251Ssam} 1494189251Ssam 1495189251Ssam 1496189251Ssam#if EAP_TTLS_VERSION > 0 1497189251Ssamstatic void eap_ttls_final_phase_finished(struct eap_sm *sm, 1498189251Ssam struct eap_ttls_data *data, 1499189251Ssam struct eap_method_ret *ret, 1500189251Ssam u8 identifier, 1501189251Ssam struct wpabuf **out_data) 1502189251Ssam{ 1503189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received"); 1504189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded"); 1505189251Ssam ret->methodState = METHOD_DONE; 1506189251Ssam ret->decision = DECISION_UNCOND_SUCC; 1507189251Ssam data->phase2_success = 1; 1508189251Ssam *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1); 1509189251Ssam eap_ttls_v1_derive_key(sm, data); 1510189251Ssam} 1511189251Ssam#endif /* EAP_TTLS_VERSION */ 1512189251Ssam 1513189251Ssam 1514189251Ssamstatic int eap_ttls_implicit_identity_request(struct eap_sm *sm, 1515189251Ssam struct eap_ttls_data *data, 1516189251Ssam struct eap_method_ret *ret, 1517189251Ssam u8 identifier, 1518189251Ssam struct wpabuf **out_data) 1519189251Ssam{ 1520189251Ssam int retval = 0; 1521189251Ssam struct eap_hdr *hdr; 1522189251Ssam struct wpabuf *resp; 1523189251Ssam 1524189251Ssam hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); 1525189251Ssam if (hdr == NULL) { 1526189251Ssam ret->methodState = METHOD_DONE; 1527189251Ssam ret->decision = DECISION_FAIL; 1528189251Ssam return -1; 1529189251Ssam } 1530189251Ssam 1531189251Ssam resp = NULL; 1532189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { 1533189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1534189251Ssam "processing failed"); 1535189251Ssam retval = -1; 1536189251Ssam } else { 1537189251Ssam retval = eap_ttls_encrypt_response(sm, data, resp, identifier, 1538189251Ssam out_data); 1539189251Ssam } 1540189251Ssam 1541189251Ssam os_free(hdr); 1542189251Ssam 1543189251Ssam if (retval < 0) { 1544189251Ssam ret->methodState = METHOD_DONE; 1545189251Ssam ret->decision = DECISION_FAIL; 1546189251Ssam } 1547189251Ssam 1548189251Ssam return retval; 1549189251Ssam} 1550189251Ssam 1551189251Ssam 1552189251Ssamstatic int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, 1553189251Ssam struct eap_method_ret *ret, u8 identifier, 1554189251Ssam struct wpabuf **out_data) 1555189251Ssam{ 1556189251Ssam data->phase2_start = 0; 1557189251Ssam 1558189251Ssam /* 1559189251Ssam * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only 1560189251Ssam * if TLS part was indeed resuming a previous session. Most 1561189251Ssam * Authentication Servers terminate EAP-TTLS before reaching this 1562189251Ssam * point, but some do not. Make wpa_supplicant stop phase 2 here, if 1563189251Ssam * needed. 1564189251Ssam */ 1565189251Ssam if (data->reauth && 1566189251Ssam tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { 1567189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " 1568189251Ssam "skip phase 2"); 1569189251Ssam *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, 1570189251Ssam data->ttls_version); 1571189251Ssam ret->methodState = METHOD_DONE; 1572189251Ssam ret->decision = DECISION_UNCOND_SUCC; 1573189251Ssam data->phase2_success = 1; 1574189251Ssam return 0; 1575189251Ssam } 1576189251Ssam 1577189251Ssam return eap_ttls_implicit_identity_request(sm, data, ret, identifier, 1578189251Ssam out_data); 1579189251Ssam} 1580189251Ssam 1581189251Ssam 1582189251Ssamstatic int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, 1583189251Ssam struct eap_method_ret *ret, u8 identifier, 1584189251Ssam const struct wpabuf *in_data, 1585189251Ssam struct wpabuf **out_data) 1586189251Ssam{ 1587189251Ssam struct wpabuf *in_decrypted = NULL; 1588189251Ssam int retval = 0; 1589189251Ssam struct ttls_parse_avp parse; 1590189251Ssam 1591189251Ssam os_memset(&parse, 0, sizeof(parse)); 1592189251Ssam 1593189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" 1594189251Ssam " Phase 2", 1595189251Ssam in_data ? (unsigned long) wpabuf_len(in_data) : 0); 1596189251Ssam 1597189251Ssam if (data->pending_phase2_req) { 1598189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " 1599189251Ssam "skip decryption and use old data"); 1600189251Ssam /* Clear TLS reassembly state. */ 1601189251Ssam eap_peer_tls_reset_input(&data->ssl); 1602189251Ssam 1603189251Ssam in_decrypted = data->pending_phase2_req; 1604189251Ssam data->pending_phase2_req = NULL; 1605189251Ssam if (wpabuf_len(in_decrypted) == 0) { 1606189251Ssam wpabuf_free(in_decrypted); 1607189251Ssam return eap_ttls_implicit_identity_request( 1608189251Ssam sm, data, ret, identifier, out_data); 1609189251Ssam } 1610189251Ssam goto continue_req; 1611189251Ssam } 1612189251Ssam 1613189251Ssam if ((in_data == NULL || wpabuf_len(in_data) == 0) && 1614189251Ssam data->phase2_start) { 1615189251Ssam return eap_ttls_phase2_start(sm, data, ret, identifier, 1616189251Ssam out_data); 1617189251Ssam } 1618189251Ssam 1619189251Ssam if (in_data == NULL || wpabuf_len(in_data) == 0) { 1620189251Ssam /* Received TLS ACK - requesting more fragments */ 1621189251Ssam return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 1622189251Ssam data->ttls_version, 1623189251Ssam identifier, NULL, out_data); 1624189251Ssam } 1625189251Ssam 1626189251Ssam retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); 1627189251Ssam if (retval) 1628189251Ssam goto done; 1629189251Ssam 1630189251Ssam#if EAP_TTLS_VERSION > 0 1631189251Ssam if (data->ttls_version > 0 && 1632189251Ssam (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) && 1633189251Ssam tls_connection_ia_final_phase_finished(sm->ssl_ctx, 1634189251Ssam data->ssl.conn)) { 1635189251Ssam eap_ttls_final_phase_finished(sm, data, ret, identifier, 1636189251Ssam out_data); 1637189251Ssam goto done; 1638189251Ssam } 1639189251Ssam#endif /* EAP_TTLS_VERSION */ 1640189251Ssam 1641189251Ssamcontinue_req: 1642189251Ssam data->phase2_start = 0; 1643189251Ssam 1644189251Ssam if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { 1645189251Ssam retval = -1; 1646189251Ssam goto done; 1647189251Ssam } 1648189251Ssam 1649189251Ssam retval = eap_ttls_process_decrypted(sm, data, ret, identifier, 1650189251Ssam &parse, in_decrypted, out_data); 1651189251Ssam 1652189251Ssamdone: 1653189251Ssam wpabuf_free(in_decrypted); 1654189251Ssam os_free(parse.eapdata); 1655189251Ssam 1656189251Ssam if (retval < 0) { 1657189251Ssam ret->methodState = METHOD_DONE; 1658189251Ssam ret->decision = DECISION_FAIL; 1659189251Ssam } 1660189251Ssam 1661189251Ssam return retval; 1662189251Ssam} 1663189251Ssam 1664189251Ssam 1665189251Ssamstatic int eap_ttls_process_start(struct eap_sm *sm, 1666189251Ssam struct eap_ttls_data *data, u8 flags, 1667189251Ssam struct eap_method_ret *ret) 1668189251Ssam{ 1669189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1670189251Ssam 1671189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)", 1672214734Srpaulo flags & EAP_TLS_VERSION_MASK, data->ttls_version); 1673189251Ssam#if EAP_TTLS_VERSION > 0 1674214734Srpaulo if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version) 1675214734Srpaulo data->ttls_version = flags & EAP_TLS_VERSION_MASK; 1676189251Ssam if (data->force_ttls_version >= 0 && 1677189251Ssam data->force_ttls_version != data->ttls_version) { 1678189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " 1679189251Ssam "forced TTLS version %d", 1680189251Ssam data->force_ttls_version); 1681189251Ssam ret->methodState = METHOD_DONE; 1682189251Ssam ret->decision = DECISION_FAIL; 1683189251Ssam ret->allowNotifications = FALSE; 1684189251Ssam return -1; 1685189251Ssam } 1686189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d", 1687189251Ssam data->ttls_version); 1688189251Ssam 1689189251Ssam if (data->ttls_version > 0) 1690189251Ssam data->ssl.tls_ia = 1; 1691189251Ssam#endif /* EAP_TTLS_VERSION */ 1692189251Ssam if (!data->ssl_initialized && 1693189251Ssam eap_peer_tls_ssl_init(sm, &data->ssl, config)) { 1694189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); 1695189251Ssam return -1; 1696189251Ssam } 1697189251Ssam data->ssl_initialized = 1; 1698189251Ssam 1699189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); 1700189251Ssam 1701189251Ssam return 0; 1702189251Ssam} 1703189251Ssam 1704189251Ssam 1705189251Ssamstatic int eap_ttls_process_handshake(struct eap_sm *sm, 1706189251Ssam struct eap_ttls_data *data, 1707189251Ssam struct eap_method_ret *ret, 1708189251Ssam u8 identifier, 1709189251Ssam const u8 *in_data, size_t in_len, 1710189251Ssam struct wpabuf **out_data) 1711189251Ssam{ 1712189251Ssam int res; 1713189251Ssam 1714189251Ssam res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, 1715189251Ssam data->ttls_version, identifier, 1716189251Ssam in_data, in_len, out_data); 1717189251Ssam 1718189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { 1719189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " 1720189251Ssam "Phase 2"); 1721189251Ssam if (data->resuming) { 1722189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " 1723189251Ssam "skip Phase 2"); 1724189251Ssam ret->decision = DECISION_COND_SUCC; 1725189251Ssam ret->methodState = METHOD_MAY_CONT; 1726189251Ssam } 1727189251Ssam data->phase2_start = 1; 1728189251Ssam if (data->ttls_version == 0) 1729189251Ssam eap_ttls_v0_derive_key(sm, data); 1730189251Ssam 1731189251Ssam if (*out_data == NULL || wpabuf_len(*out_data) == 0) { 1732189251Ssam if (eap_ttls_decrypt(sm, data, ret, identifier, 1733189251Ssam NULL, out_data)) { 1734189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: " 1735189251Ssam "failed to process early " 1736189251Ssam "start for Phase 2"); 1737189251Ssam } 1738189251Ssam res = 0; 1739189251Ssam } 1740189251Ssam data->resuming = 0; 1741189251Ssam } 1742189251Ssam 1743189251Ssam if (res == 2) { 1744189251Ssam struct wpabuf msg; 1745189251Ssam /* 1746189251Ssam * Application data included in the handshake message. 1747189251Ssam */ 1748189251Ssam wpabuf_free(data->pending_phase2_req); 1749189251Ssam data->pending_phase2_req = *out_data; 1750189251Ssam *out_data = NULL; 1751189251Ssam wpabuf_set(&msg, in_data, in_len); 1752189251Ssam res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, 1753189251Ssam out_data); 1754189251Ssam } 1755189251Ssam 1756189251Ssam return res; 1757189251Ssam} 1758189251Ssam 1759189251Ssam 1760189251Ssamstatic void eap_ttls_check_auth_status(struct eap_sm *sm, 1761189251Ssam struct eap_ttls_data *data, 1762189251Ssam struct eap_method_ret *ret) 1763189251Ssam{ 1764189251Ssam if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) { 1765189251Ssam ret->allowNotifications = FALSE; 1766189251Ssam if (ret->decision == DECISION_UNCOND_SUCC || 1767189251Ssam ret->decision == DECISION_COND_SUCC) { 1768189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1769189251Ssam "completed successfully"); 1770189251Ssam data->phase2_success = 1; 1771189251Ssam#ifdef EAP_TNC 1772189251Ssam if (!data->ready_for_tnc && !data->tnc_started) { 1773189251Ssam /* 1774189251Ssam * TNC may be required as the next 1775189251Ssam * authentication method within the tunnel. 1776189251Ssam */ 1777189251Ssam ret->methodState = METHOD_MAY_CONT; 1778189251Ssam data->ready_for_tnc = 1; 1779189251Ssam } 1780189251Ssam#endif /* EAP_TNC */ 1781189251Ssam } 1782189251Ssam } else if (data->ttls_version == 0 && 1783189251Ssam ret->methodState == METHOD_MAY_CONT && 1784189251Ssam (ret->decision == DECISION_UNCOND_SUCC || 1785189251Ssam ret->decision == DECISION_COND_SUCC)) { 1786189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1787189251Ssam "completed successfully (MAY_CONT)"); 1788189251Ssam data->phase2_success = 1; 1789189251Ssam } 1790189251Ssam} 1791189251Ssam 1792189251Ssam 1793189251Ssamstatic struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, 1794189251Ssam struct eap_method_ret *ret, 1795189251Ssam const struct wpabuf *reqData) 1796189251Ssam{ 1797189251Ssam size_t left; 1798189251Ssam int res; 1799189251Ssam u8 flags, id; 1800189251Ssam struct wpabuf *resp; 1801189251Ssam const u8 *pos; 1802189251Ssam struct eap_ttls_data *data = priv; 1803189251Ssam 1804189251Ssam pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, 1805189251Ssam reqData, &left, &flags); 1806189251Ssam if (pos == NULL) 1807189251Ssam return NULL; 1808189251Ssam id = eap_get_id(reqData); 1809189251Ssam 1810189251Ssam if (flags & EAP_TLS_FLAGS_START) { 1811189251Ssam if (eap_ttls_process_start(sm, data, flags, ret) < 0) 1812189251Ssam return NULL; 1813189251Ssam 1814189251Ssam /* RFC 5281, Ch. 9.2: 1815189251Ssam * "This packet MAY contain additional information in the form 1816189251Ssam * of AVPs, which may provide useful hints to the client" 1817189251Ssam * For now, ignore any potential extra data. 1818189251Ssam */ 1819189251Ssam left = 0; 1820189251Ssam } else if (!data->ssl_initialized) { 1821189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not " 1822189251Ssam "include Start flag"); 1823189251Ssam ret->methodState = METHOD_DONE; 1824189251Ssam ret->decision = DECISION_FAIL; 1825189251Ssam ret->allowNotifications = FALSE; 1826189251Ssam return NULL; 1827189251Ssam } 1828189251Ssam 1829189251Ssam resp = NULL; 1830189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1831189251Ssam !data->resuming) { 1832189251Ssam struct wpabuf msg; 1833189251Ssam wpabuf_set(&msg, pos, left); 1834189251Ssam res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); 1835189251Ssam } else { 1836189251Ssam res = eap_ttls_process_handshake(sm, data, ret, id, 1837189251Ssam pos, left, &resp); 1838189251Ssam } 1839189251Ssam 1840189251Ssam eap_ttls_check_auth_status(sm, data, ret); 1841189251Ssam 1842189251Ssam /* FIX: what about res == -1? Could just move all error processing into 1843189251Ssam * the other functions and get rid of this res==1 case here. */ 1844189251Ssam if (res == 1) { 1845189251Ssam wpabuf_free(resp); 1846189251Ssam return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, 1847189251Ssam data->ttls_version); 1848189251Ssam } 1849189251Ssam return resp; 1850189251Ssam} 1851189251Ssam 1852189251Ssam 1853189251Ssamstatic Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) 1854189251Ssam{ 1855189251Ssam struct eap_ttls_data *data = priv; 1856189251Ssam return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1857189251Ssam data->phase2_success; 1858189251Ssam} 1859189251Ssam 1860189251Ssam 1861189251Ssamstatic void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) 1862189251Ssam{ 1863189251Ssam struct eap_ttls_data *data = priv; 1864189251Ssam wpabuf_free(data->pending_phase2_req); 1865189251Ssam data->pending_phase2_req = NULL; 1866189251Ssam#ifdef EAP_TNC 1867189251Ssam data->ready_for_tnc = 0; 1868189251Ssam data->tnc_started = 0; 1869189251Ssam#endif /* EAP_TNC */ 1870189251Ssam} 1871189251Ssam 1872189251Ssam 1873189251Ssamstatic void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) 1874189251Ssam{ 1875189251Ssam struct eap_ttls_data *data = priv; 1876189251Ssam os_free(data->key_data); 1877189251Ssam data->key_data = NULL; 1878189251Ssam if (eap_peer_tls_reauth_init(sm, &data->ssl)) { 1879189251Ssam os_free(data); 1880189251Ssam return NULL; 1881189251Ssam } 1882189251Ssam if (data->phase2_priv && data->phase2_method && 1883189251Ssam data->phase2_method->init_for_reauth) 1884189251Ssam data->phase2_method->init_for_reauth(sm, data->phase2_priv); 1885189251Ssam data->phase2_start = 0; 1886189251Ssam data->phase2_success = 0; 1887189251Ssam data->resuming = 1; 1888189251Ssam data->reauth = 1; 1889189251Ssam return priv; 1890189251Ssam} 1891189251Ssam 1892189251Ssam 1893189251Ssamstatic int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, 1894189251Ssam size_t buflen, int verbose) 1895189251Ssam{ 1896189251Ssam struct eap_ttls_data *data = priv; 1897189251Ssam int len, ret; 1898189251Ssam 1899189251Ssam len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); 1900189251Ssam ret = os_snprintf(buf + len, buflen - len, 1901189251Ssam "EAP-TTLSv%d Phase2 method=", 1902189251Ssam data->ttls_version); 1903189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1904189251Ssam return len; 1905189251Ssam len += ret; 1906189251Ssam switch (data->phase2_type) { 1907189251Ssam case EAP_TTLS_PHASE2_EAP: 1908189251Ssam ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", 1909189251Ssam data->phase2_method ? 1910189251Ssam data->phase2_method->name : "?"); 1911189251Ssam break; 1912189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1913189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); 1914189251Ssam break; 1915189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1916189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); 1917189251Ssam break; 1918189251Ssam case EAP_TTLS_PHASE2_PAP: 1919189251Ssam ret = os_snprintf(buf + len, buflen - len, "PAP\n"); 1920189251Ssam break; 1921189251Ssam case EAP_TTLS_PHASE2_CHAP: 1922189251Ssam ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); 1923189251Ssam break; 1924189251Ssam default: 1925189251Ssam ret = 0; 1926189251Ssam break; 1927189251Ssam } 1928189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1929189251Ssam return len; 1930189251Ssam len += ret; 1931189251Ssam 1932189251Ssam return len; 1933189251Ssam} 1934189251Ssam 1935189251Ssam 1936189251Ssamstatic Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) 1937189251Ssam{ 1938189251Ssam struct eap_ttls_data *data = priv; 1939189251Ssam return data->key_data != NULL && data->phase2_success; 1940189251Ssam} 1941189251Ssam 1942189251Ssam 1943189251Ssamstatic u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) 1944189251Ssam{ 1945189251Ssam struct eap_ttls_data *data = priv; 1946189251Ssam u8 *key; 1947189251Ssam 1948189251Ssam if (data->key_data == NULL || !data->phase2_success) 1949189251Ssam return NULL; 1950189251Ssam 1951189251Ssam key = os_malloc(EAP_TLS_KEY_LEN); 1952189251Ssam if (key == NULL) 1953189251Ssam return NULL; 1954189251Ssam 1955189251Ssam *len = EAP_TLS_KEY_LEN; 1956189251Ssam os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); 1957189251Ssam 1958189251Ssam return key; 1959189251Ssam} 1960189251Ssam 1961189251Ssam 1962189251Ssamint eap_peer_ttls_register(void) 1963189251Ssam{ 1964189251Ssam struct eap_method *eap; 1965189251Ssam int ret; 1966189251Ssam 1967189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1968189251Ssam EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); 1969189251Ssam if (eap == NULL) 1970189251Ssam return -1; 1971189251Ssam 1972189251Ssam eap->init = eap_ttls_init; 1973189251Ssam eap->deinit = eap_ttls_deinit; 1974189251Ssam eap->process = eap_ttls_process; 1975189251Ssam eap->isKeyAvailable = eap_ttls_isKeyAvailable; 1976189251Ssam eap->getKey = eap_ttls_getKey; 1977189251Ssam eap->get_status = eap_ttls_get_status; 1978189251Ssam eap->has_reauth_data = eap_ttls_has_reauth_data; 1979189251Ssam eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; 1980189251Ssam eap->init_for_reauth = eap_ttls_init_for_reauth; 1981189251Ssam 1982189251Ssam ret = eap_peer_method_register(eap); 1983189251Ssam if (ret) 1984189251Ssam eap_peer_method_free(eap); 1985189251Ssam return ret; 1986189251Ssam} 1987