eap_ttls.c revision 252726
1189251Ssam/* 2189251Ssam * EAP peer method: EAP-TTLS (RFC 5281) 3252726Srpaulo * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/ms_funcs.h" 13214734Srpaulo#include "crypto/sha1.h" 14214734Srpaulo#include "crypto/tls.h" 15189251Ssam#include "eap_common/chap.h" 16214734Srpaulo#include "eap_common/eap_ttls.h" 17189251Ssam#include "mschapv2.h" 18214734Srpaulo#include "eap_i.h" 19214734Srpaulo#include "eap_tls_common.h" 20214734Srpaulo#include "eap_config.h" 21189251Ssam 22189251Ssam 23252726Srpaulo#define EAP_TTLS_VERSION 0 24189251Ssam 25189251Ssam 26189251Ssamstatic void eap_ttls_deinit(struct eap_sm *sm, void *priv); 27189251Ssam 28189251Ssam 29189251Ssamstruct eap_ttls_data { 30189251Ssam struct eap_ssl_data ssl; 31189251Ssam 32252726Srpaulo int ttls_version; 33189251Ssam 34189251Ssam const struct eap_method *phase2_method; 35189251Ssam void *phase2_priv; 36189251Ssam int phase2_success; 37189251Ssam int phase2_start; 38189251Ssam 39189251Ssam enum phase2_types { 40189251Ssam EAP_TTLS_PHASE2_EAP, 41189251Ssam EAP_TTLS_PHASE2_MSCHAPV2, 42189251Ssam EAP_TTLS_PHASE2_MSCHAP, 43189251Ssam EAP_TTLS_PHASE2_PAP, 44189251Ssam EAP_TTLS_PHASE2_CHAP 45189251Ssam } phase2_type; 46189251Ssam struct eap_method_type phase2_eap_type; 47189251Ssam struct eap_method_type *phase2_eap_types; 48189251Ssam size_t num_phase2_eap_types; 49189251Ssam 50189251Ssam u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; 51189251Ssam int auth_response_valid; 52189251Ssam u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ 53189251Ssam u8 ident; 54189251Ssam int resuming; /* starting a resumed session */ 55189251Ssam int reauth; /* reauthentication */ 56189251Ssam u8 *key_data; 57189251Ssam 58189251Ssam struct wpabuf *pending_phase2_req; 59189251Ssam 60189251Ssam#ifdef EAP_TNC 61189251Ssam int ready_for_tnc; 62189251Ssam int tnc_started; 63189251Ssam#endif /* EAP_TNC */ 64189251Ssam}; 65189251Ssam 66189251Ssam 67189251Ssamstatic void * eap_ttls_init(struct eap_sm *sm) 68189251Ssam{ 69189251Ssam struct eap_ttls_data *data; 70189251Ssam struct eap_peer_config *config = eap_get_config(sm); 71189251Ssam char *selected; 72189251Ssam 73189251Ssam data = os_zalloc(sizeof(*data)); 74189251Ssam if (data == NULL) 75189251Ssam return NULL; 76189251Ssam data->ttls_version = EAP_TTLS_VERSION; 77189251Ssam selected = "EAP"; 78189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 79189251Ssam 80189251Ssam if (config && config->phase2) { 81189251Ssam if (os_strstr(config->phase2, "autheap=")) { 82189251Ssam selected = "EAP"; 83189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 84189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { 85189251Ssam selected = "MSCHAPV2"; 86189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; 87189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAP")) { 88189251Ssam selected = "MSCHAP"; 89189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; 90189251Ssam } else if (os_strstr(config->phase2, "auth=PAP")) { 91189251Ssam selected = "PAP"; 92189251Ssam data->phase2_type = EAP_TTLS_PHASE2_PAP; 93189251Ssam } else if (os_strstr(config->phase2, "auth=CHAP")) { 94189251Ssam selected = "CHAP"; 95189251Ssam data->phase2_type = EAP_TTLS_PHASE2_CHAP; 96189251Ssam } 97189251Ssam } 98189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); 99189251Ssam 100189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { 101189251Ssam if (eap_peer_select_phase2_methods(config, "autheap=", 102189251Ssam &data->phase2_eap_types, 103189251Ssam &data->num_phase2_eap_types) 104189251Ssam < 0) { 105189251Ssam eap_ttls_deinit(sm, data); 106189251Ssam return NULL; 107189251Ssam } 108189251Ssam 109189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 110189251Ssam data->phase2_eap_type.method = EAP_TYPE_NONE; 111189251Ssam } 112189251Ssam 113252726Srpaulo if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { 114252726Srpaulo wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); 115252726Srpaulo eap_ttls_deinit(sm, data); 116252726Srpaulo return NULL; 117189251Ssam } 118189251Ssam 119189251Ssam return data; 120189251Ssam} 121189251Ssam 122189251Ssam 123189251Ssamstatic void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, 124189251Ssam struct eap_ttls_data *data) 125189251Ssam{ 126189251Ssam if (data->phase2_priv && data->phase2_method) { 127189251Ssam data->phase2_method->deinit(sm, data->phase2_priv); 128189251Ssam data->phase2_method = NULL; 129189251Ssam data->phase2_priv = NULL; 130189251Ssam } 131189251Ssam} 132189251Ssam 133189251Ssam 134189251Ssamstatic void eap_ttls_deinit(struct eap_sm *sm, void *priv) 135189251Ssam{ 136189251Ssam struct eap_ttls_data *data = priv; 137189251Ssam if (data == NULL) 138189251Ssam return; 139189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 140189251Ssam os_free(data->phase2_eap_types); 141252726Srpaulo eap_peer_tls_ssl_deinit(sm, &data->ssl); 142189251Ssam os_free(data->key_data); 143189251Ssam wpabuf_free(data->pending_phase2_req); 144189251Ssam os_free(data); 145189251Ssam} 146189251Ssam 147189251Ssam 148189251Ssamstatic u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, 149189251Ssam int mandatory, size_t len) 150189251Ssam{ 151189251Ssam struct ttls_avp_vendor *avp; 152189251Ssam u8 flags; 153189251Ssam size_t hdrlen; 154189251Ssam 155189251Ssam avp = (struct ttls_avp_vendor *) avphdr; 156189251Ssam flags = mandatory ? AVP_FLAGS_MANDATORY : 0; 157189251Ssam if (vendor_id) { 158189251Ssam flags |= AVP_FLAGS_VENDOR; 159189251Ssam hdrlen = sizeof(*avp); 160189251Ssam avp->vendor_id = host_to_be32(vendor_id); 161189251Ssam } else { 162189251Ssam hdrlen = sizeof(struct ttls_avp); 163189251Ssam } 164189251Ssam 165189251Ssam avp->avp_code = host_to_be32(avp_code); 166252726Srpaulo avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); 167189251Ssam 168189251Ssam return avphdr + hdrlen; 169189251Ssam} 170189251Ssam 171189251Ssam 172189251Ssamstatic u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, 173189251Ssam u32 vendor_id, int mandatory, 174189251Ssam const u8 *data, size_t len) 175189251Ssam{ 176189251Ssam u8 *pos; 177189251Ssam pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); 178189251Ssam os_memcpy(pos, data, len); 179189251Ssam pos += len; 180189251Ssam AVP_PAD(start, pos); 181189251Ssam return pos; 182189251Ssam} 183189251Ssam 184189251Ssam 185189251Ssamstatic int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, 186189251Ssam int mandatory) 187189251Ssam{ 188189251Ssam struct wpabuf *msg; 189189251Ssam u8 *avp, *pos; 190189251Ssam 191189251Ssam msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); 192189251Ssam if (msg == NULL) { 193189251Ssam wpabuf_free(*resp); 194189251Ssam *resp = NULL; 195189251Ssam return -1; 196189251Ssam } 197189251Ssam 198189251Ssam avp = wpabuf_mhead(msg); 199189251Ssam pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); 200189251Ssam os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); 201189251Ssam pos += wpabuf_len(*resp); 202189251Ssam AVP_PAD(avp, pos); 203189251Ssam wpabuf_free(*resp); 204189251Ssam wpabuf_put(msg, pos - avp); 205189251Ssam *resp = msg; 206189251Ssam return 0; 207189251Ssam} 208189251Ssam 209189251Ssam 210189251Ssamstatic int eap_ttls_v0_derive_key(struct eap_sm *sm, 211189251Ssam struct eap_ttls_data *data) 212189251Ssam{ 213189251Ssam os_free(data->key_data); 214189251Ssam data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, 215189251Ssam "ttls keying material", 216189251Ssam EAP_TLS_KEY_LEN); 217189251Ssam if (!data->key_data) { 218189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); 219189251Ssam return -1; 220189251Ssam } 221189251Ssam 222189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", 223189251Ssam data->key_data, EAP_TLS_KEY_LEN); 224189251Ssam 225189251Ssam return 0; 226189251Ssam} 227189251Ssam 228189251Ssam 229189251Ssamstatic u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, 230189251Ssam struct eap_ttls_data *data, size_t len) 231189251Ssam{ 232252726Srpaulo return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); 233189251Ssam} 234189251Ssam 235189251Ssam 236189251Ssamstatic void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, 237189251Ssam u8 method) 238189251Ssam{ 239189251Ssam size_t i; 240189251Ssam for (i = 0; i < data->num_phase2_eap_types; i++) { 241189251Ssam if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || 242189251Ssam data->phase2_eap_types[i].method != method) 243189251Ssam continue; 244189251Ssam 245189251Ssam data->phase2_eap_type.vendor = 246189251Ssam data->phase2_eap_types[i].vendor; 247189251Ssam data->phase2_eap_type.method = 248189251Ssam data->phase2_eap_types[i].method; 249189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 250189251Ssam "Phase 2 EAP vendor %d method %d", 251189251Ssam data->phase2_eap_type.vendor, 252189251Ssam data->phase2_eap_type.method); 253189251Ssam break; 254189251Ssam } 255189251Ssam} 256189251Ssam 257189251Ssam 258189251Ssamstatic int eap_ttls_phase2_eap_process(struct eap_sm *sm, 259189251Ssam struct eap_ttls_data *data, 260189251Ssam struct eap_method_ret *ret, 261189251Ssam struct eap_hdr *hdr, size_t len, 262189251Ssam struct wpabuf **resp) 263189251Ssam{ 264189251Ssam struct wpabuf msg; 265189251Ssam struct eap_method_ret iret; 266189251Ssam 267189251Ssam os_memset(&iret, 0, sizeof(iret)); 268189251Ssam wpabuf_set(&msg, hdr, len); 269189251Ssam *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, 270189251Ssam &msg); 271189251Ssam if ((iret.methodState == METHOD_DONE || 272189251Ssam iret.methodState == METHOD_MAY_CONT) && 273189251Ssam (iret.decision == DECISION_UNCOND_SUCC || 274189251Ssam iret.decision == DECISION_COND_SUCC || 275189251Ssam iret.decision == DECISION_FAIL)) { 276189251Ssam ret->methodState = iret.methodState; 277189251Ssam ret->decision = iret.decision; 278189251Ssam } 279189251Ssam 280189251Ssam return 0; 281189251Ssam} 282189251Ssam 283189251Ssam 284189251Ssamstatic int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, 285189251Ssam struct eap_ttls_data *data, 286189251Ssam struct eap_method_ret *ret, 287189251Ssam struct eap_hdr *hdr, size_t len, 288189251Ssam u8 method, struct wpabuf **resp) 289189251Ssam{ 290189251Ssam#ifdef EAP_TNC 291189251Ssam if (data->tnc_started && data->phase2_method && 292189251Ssam data->phase2_priv && method == EAP_TYPE_TNC && 293189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) 294189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, 295189251Ssam resp); 296189251Ssam 297189251Ssam if (data->ready_for_tnc && !data->tnc_started && 298189251Ssam method == EAP_TYPE_TNC) { 299189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 300189251Ssam "EAP method"); 301189251Ssam data->tnc_started = 1; 302189251Ssam } 303189251Ssam 304189251Ssam if (data->tnc_started) { 305189251Ssam if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || 306189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) { 307189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " 308189251Ssam "type %d for TNC", method); 309189251Ssam return -1; 310189251Ssam } 311189251Ssam 312189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 313189251Ssam data->phase2_eap_type.method = method; 314189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 315189251Ssam "Phase 2 EAP vendor %d method %d (TNC)", 316189251Ssam data->phase2_eap_type.vendor, 317189251Ssam data->phase2_eap_type.method); 318189251Ssam 319189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) 320189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 321189251Ssam } 322189251Ssam#endif /* EAP_TNC */ 323189251Ssam 324189251Ssam if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && 325189251Ssam data->phase2_eap_type.method == EAP_TYPE_NONE) 326189251Ssam eap_ttls_phase2_select_eap_method(data, method); 327189251Ssam 328189251Ssam if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) 329189251Ssam { 330189251Ssam if (eap_peer_tls_phase2_nak(data->phase2_eap_types, 331189251Ssam data->num_phase2_eap_types, 332189251Ssam hdr, resp)) 333189251Ssam return -1; 334189251Ssam return 0; 335189251Ssam } 336189251Ssam 337189251Ssam if (data->phase2_priv == NULL) { 338189251Ssam data->phase2_method = eap_peer_get_eap_method( 339189251Ssam EAP_VENDOR_IETF, method); 340189251Ssam if (data->phase2_method) { 341189251Ssam sm->init_phase2 = 1; 342189251Ssam data->phase2_priv = data->phase2_method->init(sm); 343189251Ssam sm->init_phase2 = 0; 344189251Ssam } 345189251Ssam } 346189251Ssam if (data->phase2_priv == NULL || data->phase2_method == NULL) { 347189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " 348189251Ssam "Phase 2 EAP method %d", method); 349189251Ssam return -1; 350189251Ssam } 351189251Ssam 352189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); 353189251Ssam} 354189251Ssam 355189251Ssam 356189251Ssamstatic int eap_ttls_phase2_request_eap(struct eap_sm *sm, 357189251Ssam struct eap_ttls_data *data, 358189251Ssam struct eap_method_ret *ret, 359189251Ssam struct eap_hdr *hdr, 360189251Ssam struct wpabuf **resp) 361189251Ssam{ 362189251Ssam size_t len = be_to_host16(hdr->length); 363189251Ssam u8 *pos; 364189251Ssam struct eap_peer_config *config = eap_get_config(sm); 365189251Ssam 366189251Ssam if (len <= sizeof(struct eap_hdr)) { 367189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: too short " 368189251Ssam "Phase 2 request (len=%lu)", (unsigned long) len); 369189251Ssam return -1; 370189251Ssam } 371189251Ssam pos = (u8 *) (hdr + 1); 372189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); 373189251Ssam switch (*pos) { 374189251Ssam case EAP_TYPE_IDENTITY: 375189251Ssam *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); 376189251Ssam break; 377189251Ssam default: 378189251Ssam if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, 379189251Ssam *pos, resp) < 0) 380189251Ssam return -1; 381189251Ssam break; 382189251Ssam } 383189251Ssam 384189251Ssam if (*resp == NULL && 385189251Ssam (config->pending_req_identity || config->pending_req_password || 386189251Ssam config->pending_req_otp)) { 387189251Ssam return 0; 388189251Ssam } 389189251Ssam 390189251Ssam if (*resp == NULL) 391189251Ssam return -1; 392189251Ssam 393189251Ssam wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", 394189251Ssam *resp); 395189251Ssam return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); 396189251Ssam} 397189251Ssam 398189251Ssam 399189251Ssamstatic int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, 400189251Ssam struct eap_ttls_data *data, 401189251Ssam struct eap_method_ret *ret, 402189251Ssam struct wpabuf **resp) 403189251Ssam{ 404252726Srpaulo#ifdef EAP_MSCHAPv2 405189251Ssam struct wpabuf *msg; 406189251Ssam u8 *buf, *pos, *challenge, *peer_challenge; 407189251Ssam const u8 *identity, *password; 408189251Ssam size_t identity_len, password_len; 409189251Ssam int pwhash; 410189251Ssam 411189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); 412189251Ssam 413189251Ssam identity = eap_get_config_identity(sm, &identity_len); 414189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 415189251Ssam if (identity == NULL || password == NULL) 416189251Ssam return -1; 417189251Ssam 418189251Ssam msg = wpabuf_alloc(identity_len + 1000); 419189251Ssam if (msg == NULL) { 420189251Ssam wpa_printf(MSG_ERROR, 421189251Ssam "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); 422189251Ssam return -1; 423189251Ssam } 424189251Ssam pos = buf = wpabuf_mhead(msg); 425189251Ssam 426189251Ssam /* User-Name */ 427189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 428189251Ssam identity, identity_len); 429189251Ssam 430189251Ssam /* MS-CHAP-Challenge */ 431189251Ssam challenge = eap_ttls_implicit_challenge( 432189251Ssam sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); 433189251Ssam if (challenge == NULL) { 434189251Ssam wpabuf_free(msg); 435189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 436189251Ssam "implicit challenge"); 437189251Ssam return -1; 438189251Ssam } 439189251Ssam 440189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 441189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 442189251Ssam challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); 443189251Ssam 444189251Ssam /* MS-CHAP2-Response */ 445189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, 446189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 447189251Ssam EAP_TTLS_MSCHAPV2_RESPONSE_LEN); 448189251Ssam data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; 449189251Ssam *pos++ = data->ident; 450189251Ssam *pos++ = 0; /* Flags */ 451252726Srpaulo if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { 452252726Srpaulo os_free(challenge); 453252726Srpaulo wpabuf_free(msg); 454252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " 455252726Srpaulo "random data for peer challenge"); 456252726Srpaulo return -1; 457252726Srpaulo } 458252726Srpaulo peer_challenge = pos; 459189251Ssam pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; 460189251Ssam os_memset(pos, 0, 8); /* Reserved, must be zero */ 461189251Ssam pos += 8; 462214734Srpaulo if (mschapv2_derive_response(identity, identity_len, password, 463214734Srpaulo password_len, pwhash, challenge, 464214734Srpaulo peer_challenge, pos, data->auth_response, 465214734Srpaulo data->master_key)) { 466252726Srpaulo os_free(challenge); 467214734Srpaulo wpabuf_free(msg); 468214734Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 469214734Srpaulo "response"); 470214734Srpaulo return -1; 471214734Srpaulo } 472189251Ssam data->auth_response_valid = 1; 473189251Ssam 474189251Ssam pos += 24; 475189251Ssam os_free(challenge); 476189251Ssam AVP_PAD(buf, pos); 477189251Ssam 478189251Ssam wpabuf_put(msg, pos - buf); 479189251Ssam *resp = msg; 480189251Ssam 481252726Srpaulo if (sm->workaround) { 482189251Ssam /* At least FreeRADIUS seems to be terminating 483189251Ssam * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success 484189251Ssam * packet. */ 485189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " 486189251Ssam "allow success without tunneled response"); 487189251Ssam ret->methodState = METHOD_MAY_CONT; 488189251Ssam ret->decision = DECISION_COND_SUCC; 489189251Ssam } 490189251Ssam 491189251Ssam return 0; 492252726Srpaulo#else /* EAP_MSCHAPv2 */ 493252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); 494252726Srpaulo return -1; 495252726Srpaulo#endif /* EAP_MSCHAPv2 */ 496189251Ssam} 497189251Ssam 498189251Ssam 499189251Ssamstatic int eap_ttls_phase2_request_mschap(struct eap_sm *sm, 500189251Ssam struct eap_ttls_data *data, 501189251Ssam struct eap_method_ret *ret, 502189251Ssam struct wpabuf **resp) 503189251Ssam{ 504189251Ssam struct wpabuf *msg; 505189251Ssam u8 *buf, *pos, *challenge; 506189251Ssam const u8 *identity, *password; 507189251Ssam size_t identity_len, password_len; 508189251Ssam int pwhash; 509189251Ssam 510189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); 511189251Ssam 512189251Ssam identity = eap_get_config_identity(sm, &identity_len); 513189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 514189251Ssam if (identity == NULL || password == NULL) 515189251Ssam return -1; 516189251Ssam 517189251Ssam msg = wpabuf_alloc(identity_len + 1000); 518189251Ssam if (msg == NULL) { 519189251Ssam wpa_printf(MSG_ERROR, 520189251Ssam "EAP-TTLS/MSCHAP: Failed to allocate memory"); 521189251Ssam return -1; 522189251Ssam } 523189251Ssam pos = buf = wpabuf_mhead(msg); 524189251Ssam 525189251Ssam /* User-Name */ 526189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 527189251Ssam identity, identity_len); 528189251Ssam 529189251Ssam /* MS-CHAP-Challenge */ 530189251Ssam challenge = eap_ttls_implicit_challenge( 531189251Ssam sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); 532189251Ssam if (challenge == NULL) { 533189251Ssam wpabuf_free(msg); 534189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " 535189251Ssam "implicit challenge"); 536189251Ssam return -1; 537189251Ssam } 538189251Ssam 539189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 540189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 541189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 542189251Ssam 543189251Ssam /* MS-CHAP-Response */ 544189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, 545189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 546189251Ssam EAP_TTLS_MSCHAP_RESPONSE_LEN); 547189251Ssam data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; 548189251Ssam *pos++ = data->ident; 549189251Ssam *pos++ = 1; /* Flags: Use NT style passwords */ 550189251Ssam os_memset(pos, 0, 24); /* LM-Response */ 551189251Ssam pos += 24; 552189251Ssam if (pwhash) { 553189251Ssam challenge_response(challenge, password, pos); /* NT-Response */ 554189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", 555189251Ssam password, 16); 556189251Ssam } else { 557189251Ssam nt_challenge_response(challenge, password, password_len, 558189251Ssam pos); /* NT-Response */ 559189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", 560189251Ssam password, password_len); 561189251Ssam } 562189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", 563189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 564189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); 565189251Ssam pos += 24; 566189251Ssam os_free(challenge); 567189251Ssam AVP_PAD(buf, pos); 568189251Ssam 569189251Ssam wpabuf_put(msg, pos - buf); 570189251Ssam *resp = msg; 571189251Ssam 572252726Srpaulo /* EAP-TTLS/MSCHAP does not provide tunneled success 573252726Srpaulo * notification, so assume that Phase2 succeeds. */ 574252726Srpaulo ret->methodState = METHOD_DONE; 575252726Srpaulo ret->decision = DECISION_COND_SUCC; 576189251Ssam 577189251Ssam return 0; 578189251Ssam} 579189251Ssam 580189251Ssam 581189251Ssamstatic int eap_ttls_phase2_request_pap(struct eap_sm *sm, 582189251Ssam struct eap_ttls_data *data, 583189251Ssam struct eap_method_ret *ret, 584189251Ssam struct wpabuf **resp) 585189251Ssam{ 586189251Ssam struct wpabuf *msg; 587189251Ssam u8 *buf, *pos; 588189251Ssam size_t pad; 589189251Ssam const u8 *identity, *password; 590189251Ssam size_t identity_len, password_len; 591189251Ssam 592189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); 593189251Ssam 594189251Ssam identity = eap_get_config_identity(sm, &identity_len); 595189251Ssam password = eap_get_config_password(sm, &password_len); 596189251Ssam if (identity == NULL || password == NULL) 597189251Ssam return -1; 598189251Ssam 599189251Ssam msg = wpabuf_alloc(identity_len + password_len + 100); 600189251Ssam if (msg == NULL) { 601189251Ssam wpa_printf(MSG_ERROR, 602189251Ssam "EAP-TTLS/PAP: Failed to allocate memory"); 603189251Ssam return -1; 604189251Ssam } 605189251Ssam pos = buf = wpabuf_mhead(msg); 606189251Ssam 607189251Ssam /* User-Name */ 608189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 609189251Ssam identity, identity_len); 610189251Ssam 611189251Ssam /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts 612189251Ssam * the data, so no separate encryption is used in the AVP itself. 613189251Ssam * However, the password is padded to obfuscate its length. */ 614209158Srpaulo pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; 615189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, 616189251Ssam password_len + pad); 617189251Ssam os_memcpy(pos, password, password_len); 618189251Ssam pos += password_len; 619189251Ssam os_memset(pos, 0, pad); 620189251Ssam pos += pad; 621189251Ssam AVP_PAD(buf, pos); 622189251Ssam 623189251Ssam wpabuf_put(msg, pos - buf); 624189251Ssam *resp = msg; 625189251Ssam 626252726Srpaulo /* EAP-TTLS/PAP does not provide tunneled success notification, 627252726Srpaulo * so assume that Phase2 succeeds. */ 628252726Srpaulo ret->methodState = METHOD_DONE; 629252726Srpaulo ret->decision = DECISION_COND_SUCC; 630189251Ssam 631189251Ssam return 0; 632189251Ssam} 633189251Ssam 634189251Ssam 635189251Ssamstatic int eap_ttls_phase2_request_chap(struct eap_sm *sm, 636189251Ssam struct eap_ttls_data *data, 637189251Ssam struct eap_method_ret *ret, 638189251Ssam struct wpabuf **resp) 639189251Ssam{ 640189251Ssam struct wpabuf *msg; 641189251Ssam u8 *buf, *pos, *challenge; 642189251Ssam const u8 *identity, *password; 643189251Ssam size_t identity_len, password_len; 644189251Ssam 645189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); 646189251Ssam 647189251Ssam identity = eap_get_config_identity(sm, &identity_len); 648189251Ssam password = eap_get_config_password(sm, &password_len); 649189251Ssam if (identity == NULL || password == NULL) 650189251Ssam return -1; 651189251Ssam 652189251Ssam msg = wpabuf_alloc(identity_len + 1000); 653189251Ssam if (msg == NULL) { 654189251Ssam wpa_printf(MSG_ERROR, 655189251Ssam "EAP-TTLS/CHAP: Failed to allocate memory"); 656189251Ssam return -1; 657189251Ssam } 658189251Ssam pos = buf = wpabuf_mhead(msg); 659189251Ssam 660189251Ssam /* User-Name */ 661189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 662189251Ssam identity, identity_len); 663189251Ssam 664189251Ssam /* CHAP-Challenge */ 665189251Ssam challenge = eap_ttls_implicit_challenge( 666189251Ssam sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); 667189251Ssam if (challenge == NULL) { 668189251Ssam wpabuf_free(msg); 669189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " 670189251Ssam "implicit challenge"); 671189251Ssam return -1; 672189251Ssam } 673189251Ssam 674189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, 675189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 676189251Ssam 677189251Ssam /* CHAP-Password */ 678189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, 679189251Ssam 1 + EAP_TTLS_CHAP_PASSWORD_LEN); 680189251Ssam data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; 681189251Ssam *pos++ = data->ident; 682189251Ssam 683189251Ssam /* MD5(Ident + Password + Challenge) */ 684189251Ssam chap_md5(data->ident, password, password_len, challenge, 685189251Ssam EAP_TTLS_CHAP_CHALLENGE_LEN, pos); 686189251Ssam 687189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", 688189251Ssam identity, identity_len); 689189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", 690189251Ssam password, password_len); 691189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", 692189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 693189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", 694189251Ssam pos, EAP_TTLS_CHAP_PASSWORD_LEN); 695189251Ssam pos += EAP_TTLS_CHAP_PASSWORD_LEN; 696189251Ssam os_free(challenge); 697189251Ssam AVP_PAD(buf, pos); 698189251Ssam 699189251Ssam wpabuf_put(msg, pos - buf); 700189251Ssam *resp = msg; 701189251Ssam 702252726Srpaulo /* EAP-TTLS/CHAP does not provide tunneled success 703252726Srpaulo * notification, so assume that Phase2 succeeds. */ 704252726Srpaulo ret->methodState = METHOD_DONE; 705252726Srpaulo ret->decision = DECISION_COND_SUCC; 706189251Ssam 707189251Ssam return 0; 708189251Ssam} 709189251Ssam 710189251Ssam 711189251Ssamstatic int eap_ttls_phase2_request(struct eap_sm *sm, 712189251Ssam struct eap_ttls_data *data, 713189251Ssam struct eap_method_ret *ret, 714189251Ssam struct eap_hdr *hdr, 715189251Ssam struct wpabuf **resp) 716189251Ssam{ 717189251Ssam int res = 0; 718189251Ssam size_t len; 719189251Ssam enum phase2_types phase2_type = data->phase2_type; 720189251Ssam 721189251Ssam#ifdef EAP_TNC 722189251Ssam if (data->tnc_started) { 723189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); 724189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 725189251Ssam } 726189251Ssam#endif /* EAP_TNC */ 727189251Ssam 728189251Ssam if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || 729189251Ssam phase2_type == EAP_TTLS_PHASE2_MSCHAP || 730189251Ssam phase2_type == EAP_TTLS_PHASE2_PAP || 731189251Ssam phase2_type == EAP_TTLS_PHASE2_CHAP) { 732189251Ssam if (eap_get_config_identity(sm, &len) == NULL) { 733189251Ssam wpa_printf(MSG_INFO, 734189251Ssam "EAP-TTLS: Identity not configured"); 735189251Ssam eap_sm_request_identity(sm); 736189251Ssam if (eap_get_config_password(sm, &len) == NULL) 737189251Ssam eap_sm_request_password(sm); 738189251Ssam return 0; 739189251Ssam } 740189251Ssam 741189251Ssam if (eap_get_config_password(sm, &len) == NULL) { 742189251Ssam wpa_printf(MSG_INFO, 743189251Ssam "EAP-TTLS: Password not configured"); 744189251Ssam eap_sm_request_password(sm); 745189251Ssam return 0; 746189251Ssam } 747189251Ssam } 748189251Ssam 749189251Ssam switch (phase2_type) { 750189251Ssam case EAP_TTLS_PHASE2_EAP: 751189251Ssam res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); 752189251Ssam break; 753189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 754189251Ssam res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); 755189251Ssam break; 756189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 757189251Ssam res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); 758189251Ssam break; 759189251Ssam case EAP_TTLS_PHASE2_PAP: 760189251Ssam res = eap_ttls_phase2_request_pap(sm, data, ret, resp); 761189251Ssam break; 762189251Ssam case EAP_TTLS_PHASE2_CHAP: 763189251Ssam res = eap_ttls_phase2_request_chap(sm, data, ret, resp); 764189251Ssam break; 765189251Ssam default: 766189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); 767189251Ssam res = -1; 768189251Ssam break; 769189251Ssam } 770189251Ssam 771189251Ssam if (res < 0) { 772189251Ssam ret->methodState = METHOD_DONE; 773189251Ssam ret->decision = DECISION_FAIL; 774189251Ssam } 775189251Ssam 776189251Ssam return res; 777189251Ssam} 778189251Ssam 779189251Ssam 780189251Ssamstruct ttls_parse_avp { 781189251Ssam u8 *mschapv2; 782189251Ssam u8 *eapdata; 783189251Ssam size_t eap_len; 784189251Ssam int mschapv2_error; 785189251Ssam}; 786189251Ssam 787189251Ssam 788189251Ssamstatic int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, 789189251Ssam struct ttls_parse_avp *parse) 790189251Ssam{ 791189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); 792189251Ssam if (parse->eapdata == NULL) { 793189251Ssam parse->eapdata = os_malloc(dlen); 794189251Ssam if (parse->eapdata == NULL) { 795189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 796189251Ssam "memory for Phase 2 EAP data"); 797189251Ssam return -1; 798189251Ssam } 799189251Ssam os_memcpy(parse->eapdata, dpos, dlen); 800189251Ssam parse->eap_len = dlen; 801189251Ssam } else { 802189251Ssam u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); 803189251Ssam if (neweap == NULL) { 804189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 805189251Ssam "memory for Phase 2 EAP data"); 806189251Ssam return -1; 807189251Ssam } 808189251Ssam os_memcpy(neweap + parse->eap_len, dpos, dlen); 809189251Ssam parse->eapdata = neweap; 810189251Ssam parse->eap_len += dlen; 811189251Ssam } 812189251Ssam 813189251Ssam return 0; 814189251Ssam} 815189251Ssam 816189251Ssam 817189251Ssamstatic int eap_ttls_parse_avp(u8 *pos, size_t left, 818189251Ssam struct ttls_parse_avp *parse) 819189251Ssam{ 820189251Ssam struct ttls_avp *avp; 821189251Ssam u32 avp_code, avp_length, vendor_id = 0; 822189251Ssam u8 avp_flags, *dpos; 823189251Ssam size_t dlen; 824189251Ssam 825189251Ssam avp = (struct ttls_avp *) pos; 826189251Ssam avp_code = be_to_host32(avp->avp_code); 827189251Ssam avp_length = be_to_host32(avp->avp_length); 828189251Ssam avp_flags = (avp_length >> 24) & 0xff; 829189251Ssam avp_length &= 0xffffff; 830189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " 831189251Ssam "length=%d", (int) avp_code, avp_flags, 832189251Ssam (int) avp_length); 833189251Ssam 834189251Ssam if (avp_length > left) { 835189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " 836189251Ssam "(len=%d, left=%lu) - dropped", 837189251Ssam (int) avp_length, (unsigned long) left); 838189251Ssam return -1; 839189251Ssam } 840189251Ssam 841189251Ssam if (avp_length < sizeof(*avp)) { 842189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", 843189251Ssam avp_length); 844189251Ssam return -1; 845189251Ssam } 846189251Ssam 847189251Ssam dpos = (u8 *) (avp + 1); 848189251Ssam dlen = avp_length - sizeof(*avp); 849189251Ssam if (avp_flags & AVP_FLAGS_VENDOR) { 850189251Ssam if (dlen < 4) { 851189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " 852189251Ssam "underflow"); 853189251Ssam return -1; 854189251Ssam } 855189251Ssam vendor_id = WPA_GET_BE32(dpos); 856189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", 857189251Ssam (int) vendor_id); 858189251Ssam dpos += 4; 859189251Ssam dlen -= 4; 860189251Ssam } 861189251Ssam 862189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); 863189251Ssam 864189251Ssam if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { 865189251Ssam if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) 866189251Ssam return -1; 867189251Ssam } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { 868189251Ssam /* This is an optional message that can be displayed to 869189251Ssam * the user. */ 870189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", 871189251Ssam dpos, dlen); 872189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 873189251Ssam avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { 874189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", 875189251Ssam dpos, dlen); 876189251Ssam if (dlen != 43) { 877189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " 878189251Ssam "MS-CHAP2-Success length " 879189251Ssam "(len=%lu, expected 43)", 880189251Ssam (unsigned long) dlen); 881189251Ssam return -1; 882189251Ssam } 883189251Ssam parse->mschapv2 = dpos; 884189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 885189251Ssam avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { 886189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", 887189251Ssam dpos, dlen); 888189251Ssam parse->mschapv2_error = 1; 889189251Ssam } else if (avp_flags & AVP_FLAGS_MANDATORY) { 890189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " 891189251Ssam "code %d vendor_id %d - dropped", 892189251Ssam (int) avp_code, (int) vendor_id); 893189251Ssam return -1; 894189251Ssam } else { 895189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " 896189251Ssam "code %d vendor_id %d", 897189251Ssam (int) avp_code, (int) vendor_id); 898189251Ssam } 899189251Ssam 900189251Ssam return avp_length; 901189251Ssam} 902189251Ssam 903189251Ssam 904189251Ssamstatic int eap_ttls_parse_avps(struct wpabuf *in_decrypted, 905189251Ssam struct ttls_parse_avp *parse) 906189251Ssam{ 907189251Ssam u8 *pos; 908189251Ssam size_t left, pad; 909189251Ssam int avp_length; 910189251Ssam 911189251Ssam pos = wpabuf_mhead(in_decrypted); 912189251Ssam left = wpabuf_len(in_decrypted); 913189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); 914189251Ssam if (left < sizeof(struct ttls_avp)) { 915189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" 916189251Ssam " len=%lu expected %lu or more - dropped", 917189251Ssam (unsigned long) left, 918189251Ssam (unsigned long) sizeof(struct ttls_avp)); 919189251Ssam return -1; 920189251Ssam } 921189251Ssam 922189251Ssam /* Parse AVPs */ 923189251Ssam os_memset(parse, 0, sizeof(*parse)); 924189251Ssam 925189251Ssam while (left > 0) { 926189251Ssam avp_length = eap_ttls_parse_avp(pos, left, parse); 927189251Ssam if (avp_length < 0) 928189251Ssam return -1; 929189251Ssam 930189251Ssam pad = (4 - (avp_length & 3)) & 3; 931189251Ssam pos += avp_length + pad; 932189251Ssam if (left < avp_length + pad) 933189251Ssam left = 0; 934189251Ssam else 935189251Ssam left -= avp_length + pad; 936189251Ssam } 937189251Ssam 938189251Ssam return 0; 939189251Ssam} 940189251Ssam 941189251Ssam 942189251Ssamstatic u8 * eap_ttls_fake_identity_request(void) 943189251Ssam{ 944189251Ssam struct eap_hdr *hdr; 945189251Ssam u8 *buf; 946189251Ssam 947189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " 948189251Ssam "Phase 2 - use fake EAP-Request Identity"); 949189251Ssam buf = os_malloc(sizeof(*hdr) + 1); 950189251Ssam if (buf == NULL) { 951189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " 952189251Ssam "memory for fake EAP-Identity Request"); 953189251Ssam return NULL; 954189251Ssam } 955189251Ssam 956189251Ssam hdr = (struct eap_hdr *) buf; 957189251Ssam hdr->code = EAP_CODE_REQUEST; 958189251Ssam hdr->identifier = 0; 959189251Ssam hdr->length = host_to_be16(sizeof(*hdr) + 1); 960189251Ssam buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; 961189251Ssam 962189251Ssam return buf; 963189251Ssam} 964189251Ssam 965189251Ssam 966189251Ssamstatic int eap_ttls_encrypt_response(struct eap_sm *sm, 967189251Ssam struct eap_ttls_data *data, 968189251Ssam struct wpabuf *resp, u8 identifier, 969189251Ssam struct wpabuf **out_data) 970189251Ssam{ 971189251Ssam if (resp == NULL) 972189251Ssam return 0; 973189251Ssam 974189251Ssam wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", 975189251Ssam resp); 976189251Ssam if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 977189251Ssam data->ttls_version, identifier, 978189251Ssam resp, out_data)) { 979189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " 980189251Ssam "frame"); 981189251Ssam return -1; 982189251Ssam } 983189251Ssam wpabuf_free(resp); 984189251Ssam 985189251Ssam return 0; 986189251Ssam} 987189251Ssam 988189251Ssam 989189251Ssamstatic int eap_ttls_process_phase2_eap(struct eap_sm *sm, 990189251Ssam struct eap_ttls_data *data, 991189251Ssam struct eap_method_ret *ret, 992189251Ssam struct ttls_parse_avp *parse, 993189251Ssam struct wpabuf **resp) 994189251Ssam{ 995189251Ssam struct eap_hdr *hdr; 996189251Ssam size_t len; 997189251Ssam 998189251Ssam if (parse->eapdata == NULL) { 999189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " 1000189251Ssam "packet - dropped"); 1001189251Ssam return -1; 1002189251Ssam } 1003189251Ssam 1004189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", 1005189251Ssam parse->eapdata, parse->eap_len); 1006189251Ssam hdr = (struct eap_hdr *) parse->eapdata; 1007189251Ssam 1008189251Ssam if (parse->eap_len < sizeof(*hdr)) { 1009189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " 1010189251Ssam "frame (len=%lu, expected %lu or more) - dropped", 1011189251Ssam (unsigned long) parse->eap_len, 1012189251Ssam (unsigned long) sizeof(*hdr)); 1013189251Ssam return -1; 1014189251Ssam } 1015189251Ssam len = be_to_host16(hdr->length); 1016189251Ssam if (len > parse->eap_len) { 1017189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " 1018189251Ssam "EAP frame (EAP hdr len=%lu, EAP data len in " 1019189251Ssam "AVP=%lu)", 1020189251Ssam (unsigned long) len, 1021189251Ssam (unsigned long) parse->eap_len); 1022189251Ssam return -1; 1023189251Ssam } 1024189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " 1025189251Ssam "identifier=%d length=%lu", 1026189251Ssam hdr->code, hdr->identifier, (unsigned long) len); 1027189251Ssam switch (hdr->code) { 1028189251Ssam case EAP_CODE_REQUEST: 1029189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { 1030189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1031189251Ssam "processing failed"); 1032189251Ssam return -1; 1033189251Ssam } 1034189251Ssam break; 1035189251Ssam default: 1036189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " 1037189251Ssam "Phase 2 EAP header", hdr->code); 1038189251Ssam return -1; 1039189251Ssam } 1040189251Ssam 1041189251Ssam return 0; 1042189251Ssam} 1043189251Ssam 1044189251Ssam 1045189251Ssamstatic int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, 1046189251Ssam struct eap_ttls_data *data, 1047189251Ssam struct eap_method_ret *ret, 1048189251Ssam struct ttls_parse_avp *parse) 1049189251Ssam{ 1050252726Srpaulo#ifdef EAP_MSCHAPv2 1051189251Ssam if (parse->mschapv2_error) { 1052189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " 1053189251Ssam "MS-CHAP-Error - failed"); 1054189251Ssam ret->methodState = METHOD_DONE; 1055189251Ssam ret->decision = DECISION_FAIL; 1056189251Ssam /* Reply with empty data to ACK error */ 1057189251Ssam return 1; 1058189251Ssam } 1059189251Ssam 1060189251Ssam if (parse->mschapv2 == NULL) { 1061189251Ssam#ifdef EAP_TNC 1062189251Ssam if (data->phase2_success && parse->eapdata) { 1063189251Ssam /* 1064189251Ssam * Allow EAP-TNC to be started after successfully 1065189251Ssam * completed MSCHAPV2. 1066189251Ssam */ 1067189251Ssam return 1; 1068189251Ssam } 1069189251Ssam#endif /* EAP_TNC */ 1070189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " 1071189251Ssam "received for Phase2 MSCHAPV2"); 1072189251Ssam return -1; 1073189251Ssam } 1074189251Ssam if (parse->mschapv2[0] != data->ident) { 1075189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " 1076189251Ssam "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", 1077189251Ssam parse->mschapv2[0], data->ident); 1078189251Ssam return -1; 1079189251Ssam } 1080189251Ssam if (!data->auth_response_valid || 1081189251Ssam mschapv2_verify_auth_response(data->auth_response, 1082189251Ssam parse->mschapv2 + 1, 42)) { 1083189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " 1084189251Ssam "response in Phase 2 MSCHAPV2 success request"); 1085189251Ssam return -1; 1086189251Ssam } 1087189251Ssam 1088189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " 1089189251Ssam "authentication succeeded"); 1090252726Srpaulo ret->methodState = METHOD_DONE; 1091252726Srpaulo ret->decision = DECISION_UNCOND_SUCC; 1092252726Srpaulo data->phase2_success = 1; 1093189251Ssam 1094189251Ssam /* 1095189251Ssam * Reply with empty data; authentication server will reply 1096189251Ssam * with EAP-Success after this. 1097189251Ssam */ 1098189251Ssam return 1; 1099252726Srpaulo#else /* EAP_MSCHAPv2 */ 1100252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); 1101252726Srpaulo return -1; 1102252726Srpaulo#endif /* EAP_MSCHAPv2 */ 1103189251Ssam} 1104189251Ssam 1105189251Ssam 1106189251Ssam#ifdef EAP_TNC 1107189251Ssamstatic int eap_ttls_process_tnc_start(struct eap_sm *sm, 1108189251Ssam struct eap_ttls_data *data, 1109189251Ssam struct eap_method_ret *ret, 1110189251Ssam struct ttls_parse_avp *parse, 1111189251Ssam struct wpabuf **resp) 1112189251Ssam{ 1113189251Ssam /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ 1114189251Ssam if (parse->eapdata == NULL) { 1115189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1116189251Ssam "unexpected tunneled data (no EAP)"); 1117189251Ssam return -1; 1118189251Ssam } 1119189251Ssam 1120189251Ssam if (!data->ready_for_tnc) { 1121189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1122189251Ssam "EAP after non-EAP, but not ready for TNC"); 1123189251Ssam return -1; 1124189251Ssam } 1125189251Ssam 1126189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 1127189251Ssam "non-EAP method"); 1128189251Ssam data->tnc_started = 1; 1129189251Ssam 1130189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) 1131189251Ssam return -1; 1132189251Ssam 1133189251Ssam return 0; 1134189251Ssam} 1135189251Ssam#endif /* EAP_TNC */ 1136189251Ssam 1137189251Ssam 1138189251Ssamstatic int eap_ttls_process_decrypted(struct eap_sm *sm, 1139189251Ssam struct eap_ttls_data *data, 1140189251Ssam struct eap_method_ret *ret, 1141189251Ssam u8 identifier, 1142189251Ssam struct ttls_parse_avp *parse, 1143189251Ssam struct wpabuf *in_decrypted, 1144189251Ssam struct wpabuf **out_data) 1145189251Ssam{ 1146189251Ssam struct wpabuf *resp = NULL; 1147189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1148189251Ssam int res; 1149189251Ssam enum phase2_types phase2_type = data->phase2_type; 1150189251Ssam 1151189251Ssam#ifdef EAP_TNC 1152189251Ssam if (data->tnc_started) 1153189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 1154189251Ssam#endif /* EAP_TNC */ 1155189251Ssam 1156189251Ssam switch (phase2_type) { 1157189251Ssam case EAP_TTLS_PHASE2_EAP: 1158189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < 1159189251Ssam 0) 1160189251Ssam return -1; 1161189251Ssam break; 1162189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1163189251Ssam res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); 1164189251Ssam#ifdef EAP_TNC 1165189251Ssam if (res == 1 && parse->eapdata && data->phase2_success) { 1166189251Ssam /* 1167189251Ssam * TNC may be required as the next 1168189251Ssam * authentication method within the tunnel. 1169189251Ssam */ 1170189251Ssam ret->methodState = METHOD_MAY_CONT; 1171189251Ssam data->ready_for_tnc = 1; 1172189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, 1173189251Ssam &resp) == 0) 1174189251Ssam break; 1175189251Ssam } 1176189251Ssam#endif /* EAP_TNC */ 1177189251Ssam return res; 1178189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1179189251Ssam case EAP_TTLS_PHASE2_PAP: 1180189251Ssam case EAP_TTLS_PHASE2_CHAP: 1181189251Ssam#ifdef EAP_TNC 1182189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < 1183189251Ssam 0) 1184189251Ssam return -1; 1185189251Ssam break; 1186189251Ssam#else /* EAP_TNC */ 1187189251Ssam /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled 1188189251Ssam * requests to the supplicant */ 1189189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " 1190189251Ssam "tunneled data"); 1191189251Ssam return -1; 1192189251Ssam#endif /* EAP_TNC */ 1193189251Ssam } 1194189251Ssam 1195189251Ssam if (resp) { 1196189251Ssam if (eap_ttls_encrypt_response(sm, data, resp, identifier, 1197189251Ssam out_data) < 0) 1198189251Ssam return -1; 1199189251Ssam } else if (config->pending_req_identity || 1200189251Ssam config->pending_req_password || 1201189251Ssam config->pending_req_otp || 1202189251Ssam config->pending_req_new_password) { 1203189251Ssam wpabuf_free(data->pending_phase2_req); 1204189251Ssam data->pending_phase2_req = wpabuf_dup(in_decrypted); 1205189251Ssam } 1206189251Ssam 1207189251Ssam return 0; 1208189251Ssam} 1209189251Ssam 1210189251Ssam 1211189251Ssamstatic int eap_ttls_implicit_identity_request(struct eap_sm *sm, 1212189251Ssam struct eap_ttls_data *data, 1213189251Ssam struct eap_method_ret *ret, 1214189251Ssam u8 identifier, 1215189251Ssam struct wpabuf **out_data) 1216189251Ssam{ 1217189251Ssam int retval = 0; 1218189251Ssam struct eap_hdr *hdr; 1219189251Ssam struct wpabuf *resp; 1220189251Ssam 1221189251Ssam hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); 1222189251Ssam if (hdr == NULL) { 1223189251Ssam ret->methodState = METHOD_DONE; 1224189251Ssam ret->decision = DECISION_FAIL; 1225189251Ssam return -1; 1226189251Ssam } 1227189251Ssam 1228189251Ssam resp = NULL; 1229189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { 1230189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1231189251Ssam "processing failed"); 1232189251Ssam retval = -1; 1233189251Ssam } else { 1234252726Srpaulo struct eap_peer_config *config = eap_get_config(sm); 1235252726Srpaulo if (resp == NULL && 1236252726Srpaulo (config->pending_req_identity || 1237252726Srpaulo config->pending_req_password || 1238252726Srpaulo config->pending_req_otp || 1239252726Srpaulo config->pending_req_new_password)) { 1240252726Srpaulo /* 1241252726Srpaulo * Use empty buffer to force implicit request 1242252726Srpaulo * processing when EAP request is re-processed after 1243252726Srpaulo * user input. 1244252726Srpaulo */ 1245252726Srpaulo wpabuf_free(data->pending_phase2_req); 1246252726Srpaulo data->pending_phase2_req = wpabuf_alloc(0); 1247252726Srpaulo } 1248252726Srpaulo 1249189251Ssam retval = eap_ttls_encrypt_response(sm, data, resp, identifier, 1250189251Ssam out_data); 1251189251Ssam } 1252189251Ssam 1253189251Ssam os_free(hdr); 1254189251Ssam 1255189251Ssam if (retval < 0) { 1256189251Ssam ret->methodState = METHOD_DONE; 1257189251Ssam ret->decision = DECISION_FAIL; 1258189251Ssam } 1259189251Ssam 1260189251Ssam return retval; 1261189251Ssam} 1262189251Ssam 1263189251Ssam 1264189251Ssamstatic int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, 1265189251Ssam struct eap_method_ret *ret, u8 identifier, 1266189251Ssam struct wpabuf **out_data) 1267189251Ssam{ 1268189251Ssam data->phase2_start = 0; 1269189251Ssam 1270189251Ssam /* 1271189251Ssam * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only 1272189251Ssam * if TLS part was indeed resuming a previous session. Most 1273189251Ssam * Authentication Servers terminate EAP-TTLS before reaching this 1274189251Ssam * point, but some do not. Make wpa_supplicant stop phase 2 here, if 1275189251Ssam * needed. 1276189251Ssam */ 1277189251Ssam if (data->reauth && 1278189251Ssam tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { 1279189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " 1280189251Ssam "skip phase 2"); 1281189251Ssam *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, 1282189251Ssam data->ttls_version); 1283189251Ssam ret->methodState = METHOD_DONE; 1284189251Ssam ret->decision = DECISION_UNCOND_SUCC; 1285189251Ssam data->phase2_success = 1; 1286189251Ssam return 0; 1287189251Ssam } 1288189251Ssam 1289189251Ssam return eap_ttls_implicit_identity_request(sm, data, ret, identifier, 1290189251Ssam out_data); 1291189251Ssam} 1292189251Ssam 1293189251Ssam 1294189251Ssamstatic int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, 1295189251Ssam struct eap_method_ret *ret, u8 identifier, 1296189251Ssam const struct wpabuf *in_data, 1297189251Ssam struct wpabuf **out_data) 1298189251Ssam{ 1299189251Ssam struct wpabuf *in_decrypted = NULL; 1300189251Ssam int retval = 0; 1301189251Ssam struct ttls_parse_avp parse; 1302189251Ssam 1303189251Ssam os_memset(&parse, 0, sizeof(parse)); 1304189251Ssam 1305189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" 1306189251Ssam " Phase 2", 1307189251Ssam in_data ? (unsigned long) wpabuf_len(in_data) : 0); 1308189251Ssam 1309189251Ssam if (data->pending_phase2_req) { 1310189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " 1311189251Ssam "skip decryption and use old data"); 1312189251Ssam /* Clear TLS reassembly state. */ 1313189251Ssam eap_peer_tls_reset_input(&data->ssl); 1314189251Ssam 1315189251Ssam in_decrypted = data->pending_phase2_req; 1316189251Ssam data->pending_phase2_req = NULL; 1317189251Ssam if (wpabuf_len(in_decrypted) == 0) { 1318189251Ssam wpabuf_free(in_decrypted); 1319189251Ssam return eap_ttls_implicit_identity_request( 1320189251Ssam sm, data, ret, identifier, out_data); 1321189251Ssam } 1322189251Ssam goto continue_req; 1323189251Ssam } 1324189251Ssam 1325189251Ssam if ((in_data == NULL || wpabuf_len(in_data) == 0) && 1326189251Ssam data->phase2_start) { 1327189251Ssam return eap_ttls_phase2_start(sm, data, ret, identifier, 1328189251Ssam out_data); 1329189251Ssam } 1330189251Ssam 1331189251Ssam if (in_data == NULL || wpabuf_len(in_data) == 0) { 1332189251Ssam /* Received TLS ACK - requesting more fragments */ 1333189251Ssam return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 1334189251Ssam data->ttls_version, 1335189251Ssam identifier, NULL, out_data); 1336189251Ssam } 1337189251Ssam 1338189251Ssam retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); 1339189251Ssam if (retval) 1340189251Ssam goto done; 1341189251Ssam 1342189251Ssamcontinue_req: 1343189251Ssam data->phase2_start = 0; 1344189251Ssam 1345189251Ssam if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { 1346189251Ssam retval = -1; 1347189251Ssam goto done; 1348189251Ssam } 1349189251Ssam 1350189251Ssam retval = eap_ttls_process_decrypted(sm, data, ret, identifier, 1351189251Ssam &parse, in_decrypted, out_data); 1352189251Ssam 1353189251Ssamdone: 1354189251Ssam wpabuf_free(in_decrypted); 1355189251Ssam os_free(parse.eapdata); 1356189251Ssam 1357189251Ssam if (retval < 0) { 1358189251Ssam ret->methodState = METHOD_DONE; 1359189251Ssam ret->decision = DECISION_FAIL; 1360189251Ssam } 1361189251Ssam 1362189251Ssam return retval; 1363189251Ssam} 1364189251Ssam 1365189251Ssam 1366189251Ssamstatic int eap_ttls_process_handshake(struct eap_sm *sm, 1367189251Ssam struct eap_ttls_data *data, 1368189251Ssam struct eap_method_ret *ret, 1369189251Ssam u8 identifier, 1370189251Ssam const u8 *in_data, size_t in_len, 1371189251Ssam struct wpabuf **out_data) 1372189251Ssam{ 1373189251Ssam int res; 1374189251Ssam 1375189251Ssam res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, 1376189251Ssam data->ttls_version, identifier, 1377189251Ssam in_data, in_len, out_data); 1378189251Ssam 1379189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { 1380189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " 1381189251Ssam "Phase 2"); 1382189251Ssam if (data->resuming) { 1383189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " 1384189251Ssam "skip Phase 2"); 1385189251Ssam ret->decision = DECISION_COND_SUCC; 1386189251Ssam ret->methodState = METHOD_MAY_CONT; 1387189251Ssam } 1388189251Ssam data->phase2_start = 1; 1389252726Srpaulo eap_ttls_v0_derive_key(sm, data); 1390189251Ssam 1391189251Ssam if (*out_data == NULL || wpabuf_len(*out_data) == 0) { 1392189251Ssam if (eap_ttls_decrypt(sm, data, ret, identifier, 1393189251Ssam NULL, out_data)) { 1394189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: " 1395189251Ssam "failed to process early " 1396189251Ssam "start for Phase 2"); 1397189251Ssam } 1398189251Ssam res = 0; 1399189251Ssam } 1400189251Ssam data->resuming = 0; 1401189251Ssam } 1402189251Ssam 1403189251Ssam if (res == 2) { 1404189251Ssam struct wpabuf msg; 1405189251Ssam /* 1406189251Ssam * Application data included in the handshake message. 1407189251Ssam */ 1408189251Ssam wpabuf_free(data->pending_phase2_req); 1409189251Ssam data->pending_phase2_req = *out_data; 1410189251Ssam *out_data = NULL; 1411189251Ssam wpabuf_set(&msg, in_data, in_len); 1412189251Ssam res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, 1413189251Ssam out_data); 1414189251Ssam } 1415189251Ssam 1416189251Ssam return res; 1417189251Ssam} 1418189251Ssam 1419189251Ssam 1420189251Ssamstatic void eap_ttls_check_auth_status(struct eap_sm *sm, 1421189251Ssam struct eap_ttls_data *data, 1422189251Ssam struct eap_method_ret *ret) 1423189251Ssam{ 1424252726Srpaulo if (ret->methodState == METHOD_DONE) { 1425189251Ssam ret->allowNotifications = FALSE; 1426189251Ssam if (ret->decision == DECISION_UNCOND_SUCC || 1427189251Ssam ret->decision == DECISION_COND_SUCC) { 1428189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1429189251Ssam "completed successfully"); 1430189251Ssam data->phase2_success = 1; 1431189251Ssam#ifdef EAP_TNC 1432189251Ssam if (!data->ready_for_tnc && !data->tnc_started) { 1433189251Ssam /* 1434189251Ssam * TNC may be required as the next 1435189251Ssam * authentication method within the tunnel. 1436189251Ssam */ 1437189251Ssam ret->methodState = METHOD_MAY_CONT; 1438189251Ssam data->ready_for_tnc = 1; 1439189251Ssam } 1440189251Ssam#endif /* EAP_TNC */ 1441189251Ssam } 1442252726Srpaulo } else if (ret->methodState == METHOD_MAY_CONT && 1443189251Ssam (ret->decision == DECISION_UNCOND_SUCC || 1444189251Ssam ret->decision == DECISION_COND_SUCC)) { 1445189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1446189251Ssam "completed successfully (MAY_CONT)"); 1447189251Ssam data->phase2_success = 1; 1448189251Ssam } 1449189251Ssam} 1450189251Ssam 1451189251Ssam 1452189251Ssamstatic struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, 1453189251Ssam struct eap_method_ret *ret, 1454189251Ssam const struct wpabuf *reqData) 1455189251Ssam{ 1456189251Ssam size_t left; 1457189251Ssam int res; 1458189251Ssam u8 flags, id; 1459189251Ssam struct wpabuf *resp; 1460189251Ssam const u8 *pos; 1461189251Ssam struct eap_ttls_data *data = priv; 1462189251Ssam 1463189251Ssam pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, 1464189251Ssam reqData, &left, &flags); 1465189251Ssam if (pos == NULL) 1466189251Ssam return NULL; 1467189251Ssam id = eap_get_id(reqData); 1468189251Ssam 1469189251Ssam if (flags & EAP_TLS_FLAGS_START) { 1470252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " 1471252726Srpaulo "ver=%d)", flags & EAP_TLS_VERSION_MASK, 1472252726Srpaulo data->ttls_version); 1473189251Ssam 1474189251Ssam /* RFC 5281, Ch. 9.2: 1475189251Ssam * "This packet MAY contain additional information in the form 1476189251Ssam * of AVPs, which may provide useful hints to the client" 1477189251Ssam * For now, ignore any potential extra data. 1478189251Ssam */ 1479189251Ssam left = 0; 1480189251Ssam } 1481189251Ssam 1482189251Ssam resp = NULL; 1483189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1484189251Ssam !data->resuming) { 1485189251Ssam struct wpabuf msg; 1486189251Ssam wpabuf_set(&msg, pos, left); 1487189251Ssam res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); 1488189251Ssam } else { 1489189251Ssam res = eap_ttls_process_handshake(sm, data, ret, id, 1490189251Ssam pos, left, &resp); 1491189251Ssam } 1492189251Ssam 1493189251Ssam eap_ttls_check_auth_status(sm, data, ret); 1494189251Ssam 1495189251Ssam /* FIX: what about res == -1? Could just move all error processing into 1496189251Ssam * the other functions and get rid of this res==1 case here. */ 1497189251Ssam if (res == 1) { 1498189251Ssam wpabuf_free(resp); 1499189251Ssam return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, 1500189251Ssam data->ttls_version); 1501189251Ssam } 1502189251Ssam return resp; 1503189251Ssam} 1504189251Ssam 1505189251Ssam 1506189251Ssamstatic Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) 1507189251Ssam{ 1508189251Ssam struct eap_ttls_data *data = priv; 1509189251Ssam return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1510189251Ssam data->phase2_success; 1511189251Ssam} 1512189251Ssam 1513189251Ssam 1514189251Ssamstatic void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) 1515189251Ssam{ 1516189251Ssam struct eap_ttls_data *data = priv; 1517189251Ssam wpabuf_free(data->pending_phase2_req); 1518189251Ssam data->pending_phase2_req = NULL; 1519189251Ssam#ifdef EAP_TNC 1520189251Ssam data->ready_for_tnc = 0; 1521189251Ssam data->tnc_started = 0; 1522189251Ssam#endif /* EAP_TNC */ 1523189251Ssam} 1524189251Ssam 1525189251Ssam 1526189251Ssamstatic void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) 1527189251Ssam{ 1528189251Ssam struct eap_ttls_data *data = priv; 1529189251Ssam os_free(data->key_data); 1530189251Ssam data->key_data = NULL; 1531189251Ssam if (eap_peer_tls_reauth_init(sm, &data->ssl)) { 1532189251Ssam os_free(data); 1533189251Ssam return NULL; 1534189251Ssam } 1535189251Ssam if (data->phase2_priv && data->phase2_method && 1536189251Ssam data->phase2_method->init_for_reauth) 1537189251Ssam data->phase2_method->init_for_reauth(sm, data->phase2_priv); 1538189251Ssam data->phase2_start = 0; 1539189251Ssam data->phase2_success = 0; 1540189251Ssam data->resuming = 1; 1541189251Ssam data->reauth = 1; 1542189251Ssam return priv; 1543189251Ssam} 1544189251Ssam 1545189251Ssam 1546189251Ssamstatic int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, 1547189251Ssam size_t buflen, int verbose) 1548189251Ssam{ 1549189251Ssam struct eap_ttls_data *data = priv; 1550189251Ssam int len, ret; 1551189251Ssam 1552189251Ssam len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); 1553189251Ssam ret = os_snprintf(buf + len, buflen - len, 1554189251Ssam "EAP-TTLSv%d Phase2 method=", 1555189251Ssam data->ttls_version); 1556189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1557189251Ssam return len; 1558189251Ssam len += ret; 1559189251Ssam switch (data->phase2_type) { 1560189251Ssam case EAP_TTLS_PHASE2_EAP: 1561189251Ssam ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", 1562189251Ssam data->phase2_method ? 1563189251Ssam data->phase2_method->name : "?"); 1564189251Ssam break; 1565189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1566189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); 1567189251Ssam break; 1568189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1569189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); 1570189251Ssam break; 1571189251Ssam case EAP_TTLS_PHASE2_PAP: 1572189251Ssam ret = os_snprintf(buf + len, buflen - len, "PAP\n"); 1573189251Ssam break; 1574189251Ssam case EAP_TTLS_PHASE2_CHAP: 1575189251Ssam ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); 1576189251Ssam break; 1577189251Ssam default: 1578189251Ssam ret = 0; 1579189251Ssam break; 1580189251Ssam } 1581189251Ssam if (ret < 0 || (size_t) ret >= buflen - len) 1582189251Ssam return len; 1583189251Ssam len += ret; 1584189251Ssam 1585189251Ssam return len; 1586189251Ssam} 1587189251Ssam 1588189251Ssam 1589189251Ssamstatic Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) 1590189251Ssam{ 1591189251Ssam struct eap_ttls_data *data = priv; 1592189251Ssam return data->key_data != NULL && data->phase2_success; 1593189251Ssam} 1594189251Ssam 1595189251Ssam 1596189251Ssamstatic u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) 1597189251Ssam{ 1598189251Ssam struct eap_ttls_data *data = priv; 1599189251Ssam u8 *key; 1600189251Ssam 1601189251Ssam if (data->key_data == NULL || !data->phase2_success) 1602189251Ssam return NULL; 1603189251Ssam 1604189251Ssam key = os_malloc(EAP_TLS_KEY_LEN); 1605189251Ssam if (key == NULL) 1606189251Ssam return NULL; 1607189251Ssam 1608189251Ssam *len = EAP_TLS_KEY_LEN; 1609189251Ssam os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); 1610189251Ssam 1611189251Ssam return key; 1612189251Ssam} 1613189251Ssam 1614189251Ssam 1615189251Ssamint eap_peer_ttls_register(void) 1616189251Ssam{ 1617189251Ssam struct eap_method *eap; 1618189251Ssam int ret; 1619189251Ssam 1620189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1621189251Ssam EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); 1622189251Ssam if (eap == NULL) 1623189251Ssam return -1; 1624189251Ssam 1625189251Ssam eap->init = eap_ttls_init; 1626189251Ssam eap->deinit = eap_ttls_deinit; 1627189251Ssam eap->process = eap_ttls_process; 1628189251Ssam eap->isKeyAvailable = eap_ttls_isKeyAvailable; 1629189251Ssam eap->getKey = eap_ttls_getKey; 1630189251Ssam eap->get_status = eap_ttls_get_status; 1631189251Ssam eap->has_reauth_data = eap_ttls_has_reauth_data; 1632189251Ssam eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; 1633189251Ssam eap->init_for_reauth = eap_ttls_init_for_reauth; 1634189251Ssam 1635189251Ssam ret = eap_peer_method_register(eap); 1636189251Ssam if (ret) 1637189251Ssam eap_peer_method_free(eap); 1638189251Ssam return ret; 1639189251Ssam} 1640