eap_ttls.c revision 281806
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; 57281806Srpaulo u8 *session_id; 58281806Srpaulo size_t id_len; 59189251Ssam 60189251Ssam struct wpabuf *pending_phase2_req; 61189251Ssam 62189251Ssam#ifdef EAP_TNC 63189251Ssam int ready_for_tnc; 64189251Ssam int tnc_started; 65189251Ssam#endif /* EAP_TNC */ 66189251Ssam}; 67189251Ssam 68189251Ssam 69189251Ssamstatic void * eap_ttls_init(struct eap_sm *sm) 70189251Ssam{ 71189251Ssam struct eap_ttls_data *data; 72189251Ssam struct eap_peer_config *config = eap_get_config(sm); 73189251Ssam char *selected; 74189251Ssam 75189251Ssam data = os_zalloc(sizeof(*data)); 76189251Ssam if (data == NULL) 77189251Ssam return NULL; 78189251Ssam data->ttls_version = EAP_TTLS_VERSION; 79189251Ssam selected = "EAP"; 80189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 81189251Ssam 82189251Ssam if (config && config->phase2) { 83189251Ssam if (os_strstr(config->phase2, "autheap=")) { 84189251Ssam selected = "EAP"; 85189251Ssam data->phase2_type = EAP_TTLS_PHASE2_EAP; 86189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { 87189251Ssam selected = "MSCHAPV2"; 88189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; 89189251Ssam } else if (os_strstr(config->phase2, "auth=MSCHAP")) { 90189251Ssam selected = "MSCHAP"; 91189251Ssam data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; 92189251Ssam } else if (os_strstr(config->phase2, "auth=PAP")) { 93189251Ssam selected = "PAP"; 94189251Ssam data->phase2_type = EAP_TTLS_PHASE2_PAP; 95189251Ssam } else if (os_strstr(config->phase2, "auth=CHAP")) { 96189251Ssam selected = "CHAP"; 97189251Ssam data->phase2_type = EAP_TTLS_PHASE2_CHAP; 98189251Ssam } 99189251Ssam } 100189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); 101189251Ssam 102189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { 103189251Ssam if (eap_peer_select_phase2_methods(config, "autheap=", 104189251Ssam &data->phase2_eap_types, 105189251Ssam &data->num_phase2_eap_types) 106189251Ssam < 0) { 107189251Ssam eap_ttls_deinit(sm, data); 108189251Ssam return NULL; 109189251Ssam } 110189251Ssam 111189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 112189251Ssam data->phase2_eap_type.method = EAP_TYPE_NONE; 113189251Ssam } 114189251Ssam 115252726Srpaulo if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { 116252726Srpaulo wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); 117252726Srpaulo eap_ttls_deinit(sm, data); 118252726Srpaulo return NULL; 119189251Ssam } 120189251Ssam 121189251Ssam return data; 122189251Ssam} 123189251Ssam 124189251Ssam 125189251Ssamstatic void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, 126189251Ssam struct eap_ttls_data *data) 127189251Ssam{ 128189251Ssam if (data->phase2_priv && data->phase2_method) { 129189251Ssam data->phase2_method->deinit(sm, data->phase2_priv); 130189251Ssam data->phase2_method = NULL; 131189251Ssam data->phase2_priv = NULL; 132189251Ssam } 133189251Ssam} 134189251Ssam 135189251Ssam 136281806Srpaulostatic void eap_ttls_free_key(struct eap_ttls_data *data) 137281806Srpaulo{ 138281806Srpaulo if (data->key_data) { 139281806Srpaulo bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 140281806Srpaulo data->key_data = NULL; 141281806Srpaulo } 142281806Srpaulo} 143281806Srpaulo 144281806Srpaulo 145189251Ssamstatic void eap_ttls_deinit(struct eap_sm *sm, void *priv) 146189251Ssam{ 147189251Ssam struct eap_ttls_data *data = priv; 148189251Ssam if (data == NULL) 149189251Ssam return; 150189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 151189251Ssam os_free(data->phase2_eap_types); 152252726Srpaulo eap_peer_tls_ssl_deinit(sm, &data->ssl); 153281806Srpaulo eap_ttls_free_key(data); 154281806Srpaulo os_free(data->session_id); 155189251Ssam wpabuf_free(data->pending_phase2_req); 156189251Ssam os_free(data); 157189251Ssam} 158189251Ssam 159189251Ssam 160189251Ssamstatic u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, 161189251Ssam int mandatory, size_t len) 162189251Ssam{ 163189251Ssam struct ttls_avp_vendor *avp; 164189251Ssam u8 flags; 165189251Ssam size_t hdrlen; 166189251Ssam 167189251Ssam avp = (struct ttls_avp_vendor *) avphdr; 168189251Ssam flags = mandatory ? AVP_FLAGS_MANDATORY : 0; 169189251Ssam if (vendor_id) { 170189251Ssam flags |= AVP_FLAGS_VENDOR; 171189251Ssam hdrlen = sizeof(*avp); 172189251Ssam avp->vendor_id = host_to_be32(vendor_id); 173189251Ssam } else { 174189251Ssam hdrlen = sizeof(struct ttls_avp); 175189251Ssam } 176189251Ssam 177189251Ssam avp->avp_code = host_to_be32(avp_code); 178252726Srpaulo avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); 179189251Ssam 180189251Ssam return avphdr + hdrlen; 181189251Ssam} 182189251Ssam 183189251Ssam 184189251Ssamstatic u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, 185189251Ssam u32 vendor_id, int mandatory, 186189251Ssam const u8 *data, size_t len) 187189251Ssam{ 188189251Ssam u8 *pos; 189189251Ssam pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); 190189251Ssam os_memcpy(pos, data, len); 191189251Ssam pos += len; 192189251Ssam AVP_PAD(start, pos); 193189251Ssam return pos; 194189251Ssam} 195189251Ssam 196189251Ssam 197189251Ssamstatic int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, 198189251Ssam int mandatory) 199189251Ssam{ 200189251Ssam struct wpabuf *msg; 201189251Ssam u8 *avp, *pos; 202189251Ssam 203189251Ssam msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); 204189251Ssam if (msg == NULL) { 205189251Ssam wpabuf_free(*resp); 206189251Ssam *resp = NULL; 207189251Ssam return -1; 208189251Ssam } 209189251Ssam 210189251Ssam avp = wpabuf_mhead(msg); 211189251Ssam pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); 212189251Ssam os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); 213189251Ssam pos += wpabuf_len(*resp); 214189251Ssam AVP_PAD(avp, pos); 215189251Ssam wpabuf_free(*resp); 216189251Ssam wpabuf_put(msg, pos - avp); 217189251Ssam *resp = msg; 218189251Ssam return 0; 219189251Ssam} 220189251Ssam 221189251Ssam 222189251Ssamstatic int eap_ttls_v0_derive_key(struct eap_sm *sm, 223189251Ssam struct eap_ttls_data *data) 224189251Ssam{ 225281806Srpaulo eap_ttls_free_key(data); 226189251Ssam data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, 227189251Ssam "ttls keying material", 228281806Srpaulo EAP_TLS_KEY_LEN + 229281806Srpaulo EAP_EMSK_LEN); 230189251Ssam if (!data->key_data) { 231189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); 232189251Ssam return -1; 233189251Ssam } 234189251Ssam 235189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", 236189251Ssam data->key_data, EAP_TLS_KEY_LEN); 237281806Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK", 238281806Srpaulo data->key_data + EAP_TLS_KEY_LEN, 239281806Srpaulo EAP_EMSK_LEN); 240189251Ssam 241281806Srpaulo os_free(data->session_id); 242281806Srpaulo data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, 243281806Srpaulo EAP_TYPE_TTLS, 244281806Srpaulo &data->id_len); 245281806Srpaulo if (data->session_id) { 246281806Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", 247281806Srpaulo data->session_id, data->id_len); 248281806Srpaulo } else { 249281806Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); 250281806Srpaulo } 251281806Srpaulo 252189251Ssam return 0; 253189251Ssam} 254189251Ssam 255189251Ssam 256189251Ssamstatic u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, 257189251Ssam struct eap_ttls_data *data, size_t len) 258189251Ssam{ 259252726Srpaulo return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); 260189251Ssam} 261189251Ssam 262189251Ssam 263189251Ssamstatic void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, 264189251Ssam u8 method) 265189251Ssam{ 266189251Ssam size_t i; 267189251Ssam for (i = 0; i < data->num_phase2_eap_types; i++) { 268189251Ssam if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || 269189251Ssam data->phase2_eap_types[i].method != method) 270189251Ssam continue; 271189251Ssam 272189251Ssam data->phase2_eap_type.vendor = 273189251Ssam data->phase2_eap_types[i].vendor; 274189251Ssam data->phase2_eap_type.method = 275189251Ssam data->phase2_eap_types[i].method; 276189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 277189251Ssam "Phase 2 EAP vendor %d method %d", 278189251Ssam data->phase2_eap_type.vendor, 279189251Ssam data->phase2_eap_type.method); 280189251Ssam break; 281189251Ssam } 282189251Ssam} 283189251Ssam 284189251Ssam 285189251Ssamstatic int eap_ttls_phase2_eap_process(struct eap_sm *sm, 286189251Ssam struct eap_ttls_data *data, 287189251Ssam struct eap_method_ret *ret, 288189251Ssam struct eap_hdr *hdr, size_t len, 289189251Ssam struct wpabuf **resp) 290189251Ssam{ 291189251Ssam struct wpabuf msg; 292189251Ssam struct eap_method_ret iret; 293189251Ssam 294189251Ssam os_memset(&iret, 0, sizeof(iret)); 295189251Ssam wpabuf_set(&msg, hdr, len); 296189251Ssam *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, 297189251Ssam &msg); 298189251Ssam if ((iret.methodState == METHOD_DONE || 299189251Ssam iret.methodState == METHOD_MAY_CONT) && 300189251Ssam (iret.decision == DECISION_UNCOND_SUCC || 301189251Ssam iret.decision == DECISION_COND_SUCC || 302189251Ssam iret.decision == DECISION_FAIL)) { 303189251Ssam ret->methodState = iret.methodState; 304189251Ssam ret->decision = iret.decision; 305189251Ssam } 306189251Ssam 307189251Ssam return 0; 308189251Ssam} 309189251Ssam 310189251Ssam 311189251Ssamstatic int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, 312189251Ssam struct eap_ttls_data *data, 313189251Ssam struct eap_method_ret *ret, 314189251Ssam struct eap_hdr *hdr, size_t len, 315189251Ssam u8 method, struct wpabuf **resp) 316189251Ssam{ 317189251Ssam#ifdef EAP_TNC 318189251Ssam if (data->tnc_started && data->phase2_method && 319189251Ssam data->phase2_priv && method == EAP_TYPE_TNC && 320189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) 321189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, 322189251Ssam resp); 323189251Ssam 324189251Ssam if (data->ready_for_tnc && !data->tnc_started && 325189251Ssam method == EAP_TYPE_TNC) { 326189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 327189251Ssam "EAP method"); 328189251Ssam data->tnc_started = 1; 329189251Ssam } 330189251Ssam 331189251Ssam if (data->tnc_started) { 332189251Ssam if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || 333189251Ssam data->phase2_eap_type.method == EAP_TYPE_TNC) { 334189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " 335189251Ssam "type %d for TNC", method); 336189251Ssam return -1; 337189251Ssam } 338189251Ssam 339189251Ssam data->phase2_eap_type.vendor = EAP_VENDOR_IETF; 340189251Ssam data->phase2_eap_type.method = method; 341189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " 342189251Ssam "Phase 2 EAP vendor %d method %d (TNC)", 343189251Ssam data->phase2_eap_type.vendor, 344189251Ssam data->phase2_eap_type.method); 345189251Ssam 346189251Ssam if (data->phase2_type == EAP_TTLS_PHASE2_EAP) 347189251Ssam eap_ttls_phase2_eap_deinit(sm, data); 348189251Ssam } 349189251Ssam#endif /* EAP_TNC */ 350189251Ssam 351189251Ssam if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && 352189251Ssam data->phase2_eap_type.method == EAP_TYPE_NONE) 353189251Ssam eap_ttls_phase2_select_eap_method(data, method); 354189251Ssam 355189251Ssam if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) 356189251Ssam { 357189251Ssam if (eap_peer_tls_phase2_nak(data->phase2_eap_types, 358189251Ssam data->num_phase2_eap_types, 359189251Ssam hdr, resp)) 360189251Ssam return -1; 361189251Ssam return 0; 362189251Ssam } 363189251Ssam 364189251Ssam if (data->phase2_priv == NULL) { 365189251Ssam data->phase2_method = eap_peer_get_eap_method( 366189251Ssam EAP_VENDOR_IETF, method); 367189251Ssam if (data->phase2_method) { 368189251Ssam sm->init_phase2 = 1; 369189251Ssam data->phase2_priv = data->phase2_method->init(sm); 370189251Ssam sm->init_phase2 = 0; 371189251Ssam } 372189251Ssam } 373189251Ssam if (data->phase2_priv == NULL || data->phase2_method == NULL) { 374189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " 375189251Ssam "Phase 2 EAP method %d", method); 376189251Ssam return -1; 377189251Ssam } 378189251Ssam 379189251Ssam return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); 380189251Ssam} 381189251Ssam 382189251Ssam 383189251Ssamstatic int eap_ttls_phase2_request_eap(struct eap_sm *sm, 384189251Ssam struct eap_ttls_data *data, 385189251Ssam struct eap_method_ret *ret, 386189251Ssam struct eap_hdr *hdr, 387189251Ssam struct wpabuf **resp) 388189251Ssam{ 389189251Ssam size_t len = be_to_host16(hdr->length); 390189251Ssam u8 *pos; 391189251Ssam struct eap_peer_config *config = eap_get_config(sm); 392189251Ssam 393189251Ssam if (len <= sizeof(struct eap_hdr)) { 394189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: too short " 395189251Ssam "Phase 2 request (len=%lu)", (unsigned long) len); 396189251Ssam return -1; 397189251Ssam } 398189251Ssam pos = (u8 *) (hdr + 1); 399189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); 400189251Ssam switch (*pos) { 401189251Ssam case EAP_TYPE_IDENTITY: 402189251Ssam *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); 403189251Ssam break; 404189251Ssam default: 405189251Ssam if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, 406189251Ssam *pos, resp) < 0) 407189251Ssam return -1; 408189251Ssam break; 409189251Ssam } 410189251Ssam 411189251Ssam if (*resp == NULL && 412189251Ssam (config->pending_req_identity || config->pending_req_password || 413189251Ssam config->pending_req_otp)) { 414189251Ssam return 0; 415189251Ssam } 416189251Ssam 417189251Ssam if (*resp == NULL) 418189251Ssam return -1; 419189251Ssam 420189251Ssam wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", 421189251Ssam *resp); 422189251Ssam return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); 423189251Ssam} 424189251Ssam 425189251Ssam 426189251Ssamstatic int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, 427189251Ssam struct eap_ttls_data *data, 428189251Ssam struct eap_method_ret *ret, 429189251Ssam struct wpabuf **resp) 430189251Ssam{ 431252726Srpaulo#ifdef EAP_MSCHAPv2 432189251Ssam struct wpabuf *msg; 433189251Ssam u8 *buf, *pos, *challenge, *peer_challenge; 434189251Ssam const u8 *identity, *password; 435189251Ssam size_t identity_len, password_len; 436189251Ssam int pwhash; 437189251Ssam 438189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); 439189251Ssam 440189251Ssam identity = eap_get_config_identity(sm, &identity_len); 441189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 442189251Ssam if (identity == NULL || password == NULL) 443189251Ssam return -1; 444189251Ssam 445189251Ssam msg = wpabuf_alloc(identity_len + 1000); 446189251Ssam if (msg == NULL) { 447189251Ssam wpa_printf(MSG_ERROR, 448189251Ssam "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); 449189251Ssam return -1; 450189251Ssam } 451189251Ssam pos = buf = wpabuf_mhead(msg); 452189251Ssam 453189251Ssam /* User-Name */ 454189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 455189251Ssam identity, identity_len); 456189251Ssam 457189251Ssam /* MS-CHAP-Challenge */ 458189251Ssam challenge = eap_ttls_implicit_challenge( 459189251Ssam sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); 460189251Ssam if (challenge == NULL) { 461189251Ssam wpabuf_free(msg); 462189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 463189251Ssam "implicit challenge"); 464189251Ssam return -1; 465189251Ssam } 466189251Ssam 467189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 468189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 469189251Ssam challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); 470189251Ssam 471189251Ssam /* MS-CHAP2-Response */ 472189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, 473189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 474189251Ssam EAP_TTLS_MSCHAPV2_RESPONSE_LEN); 475189251Ssam data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; 476189251Ssam *pos++ = data->ident; 477189251Ssam *pos++ = 0; /* Flags */ 478252726Srpaulo if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { 479252726Srpaulo os_free(challenge); 480252726Srpaulo wpabuf_free(msg); 481252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " 482252726Srpaulo "random data for peer challenge"); 483252726Srpaulo return -1; 484252726Srpaulo } 485252726Srpaulo peer_challenge = pos; 486189251Ssam pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; 487189251Ssam os_memset(pos, 0, 8); /* Reserved, must be zero */ 488189251Ssam pos += 8; 489214734Srpaulo if (mschapv2_derive_response(identity, identity_len, password, 490214734Srpaulo password_len, pwhash, challenge, 491214734Srpaulo peer_challenge, pos, data->auth_response, 492214734Srpaulo data->master_key)) { 493252726Srpaulo os_free(challenge); 494214734Srpaulo wpabuf_free(msg); 495214734Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " 496214734Srpaulo "response"); 497214734Srpaulo return -1; 498214734Srpaulo } 499189251Ssam data->auth_response_valid = 1; 500189251Ssam 501189251Ssam pos += 24; 502189251Ssam os_free(challenge); 503189251Ssam AVP_PAD(buf, pos); 504189251Ssam 505189251Ssam wpabuf_put(msg, pos - buf); 506189251Ssam *resp = msg; 507189251Ssam 508189251Ssam return 0; 509252726Srpaulo#else /* EAP_MSCHAPv2 */ 510252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); 511252726Srpaulo return -1; 512252726Srpaulo#endif /* EAP_MSCHAPv2 */ 513189251Ssam} 514189251Ssam 515189251Ssam 516189251Ssamstatic int eap_ttls_phase2_request_mschap(struct eap_sm *sm, 517189251Ssam struct eap_ttls_data *data, 518189251Ssam struct eap_method_ret *ret, 519189251Ssam struct wpabuf **resp) 520189251Ssam{ 521189251Ssam struct wpabuf *msg; 522189251Ssam u8 *buf, *pos, *challenge; 523189251Ssam const u8 *identity, *password; 524189251Ssam size_t identity_len, password_len; 525189251Ssam int pwhash; 526189251Ssam 527189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); 528189251Ssam 529189251Ssam identity = eap_get_config_identity(sm, &identity_len); 530189251Ssam password = eap_get_config_password2(sm, &password_len, &pwhash); 531189251Ssam if (identity == NULL || password == NULL) 532189251Ssam return -1; 533189251Ssam 534189251Ssam msg = wpabuf_alloc(identity_len + 1000); 535189251Ssam if (msg == NULL) { 536189251Ssam wpa_printf(MSG_ERROR, 537189251Ssam "EAP-TTLS/MSCHAP: Failed to allocate memory"); 538189251Ssam return -1; 539189251Ssam } 540189251Ssam pos = buf = wpabuf_mhead(msg); 541189251Ssam 542189251Ssam /* User-Name */ 543189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 544189251Ssam identity, identity_len); 545189251Ssam 546189251Ssam /* MS-CHAP-Challenge */ 547189251Ssam challenge = eap_ttls_implicit_challenge( 548189251Ssam sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); 549189251Ssam if (challenge == NULL) { 550189251Ssam wpabuf_free(msg); 551189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " 552189251Ssam "implicit challenge"); 553189251Ssam return -1; 554189251Ssam } 555189251Ssam 556189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, 557189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 558189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 559189251Ssam 560189251Ssam /* MS-CHAP-Response */ 561189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, 562189251Ssam RADIUS_VENDOR_ID_MICROSOFT, 1, 563189251Ssam EAP_TTLS_MSCHAP_RESPONSE_LEN); 564189251Ssam data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; 565189251Ssam *pos++ = data->ident; 566189251Ssam *pos++ = 1; /* Flags: Use NT style passwords */ 567189251Ssam os_memset(pos, 0, 24); /* LM-Response */ 568189251Ssam pos += 24; 569189251Ssam if (pwhash) { 570189251Ssam challenge_response(challenge, password, pos); /* NT-Response */ 571189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", 572189251Ssam password, 16); 573189251Ssam } else { 574189251Ssam nt_challenge_response(challenge, password, password_len, 575189251Ssam pos); /* NT-Response */ 576189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", 577189251Ssam password, password_len); 578189251Ssam } 579189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", 580189251Ssam challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); 581189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); 582189251Ssam pos += 24; 583189251Ssam os_free(challenge); 584189251Ssam AVP_PAD(buf, pos); 585189251Ssam 586189251Ssam wpabuf_put(msg, pos - buf); 587189251Ssam *resp = msg; 588189251Ssam 589252726Srpaulo /* EAP-TTLS/MSCHAP does not provide tunneled success 590252726Srpaulo * notification, so assume that Phase2 succeeds. */ 591252726Srpaulo ret->methodState = METHOD_DONE; 592252726Srpaulo ret->decision = DECISION_COND_SUCC; 593189251Ssam 594189251Ssam return 0; 595189251Ssam} 596189251Ssam 597189251Ssam 598189251Ssamstatic int eap_ttls_phase2_request_pap(struct eap_sm *sm, 599189251Ssam struct eap_ttls_data *data, 600189251Ssam struct eap_method_ret *ret, 601189251Ssam struct wpabuf **resp) 602189251Ssam{ 603189251Ssam struct wpabuf *msg; 604189251Ssam u8 *buf, *pos; 605189251Ssam size_t pad; 606189251Ssam const u8 *identity, *password; 607189251Ssam size_t identity_len, password_len; 608189251Ssam 609189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); 610189251Ssam 611189251Ssam identity = eap_get_config_identity(sm, &identity_len); 612189251Ssam password = eap_get_config_password(sm, &password_len); 613189251Ssam if (identity == NULL || password == NULL) 614189251Ssam return -1; 615189251Ssam 616189251Ssam msg = wpabuf_alloc(identity_len + password_len + 100); 617189251Ssam if (msg == NULL) { 618189251Ssam wpa_printf(MSG_ERROR, 619189251Ssam "EAP-TTLS/PAP: Failed to allocate memory"); 620189251Ssam return -1; 621189251Ssam } 622189251Ssam pos = buf = wpabuf_mhead(msg); 623189251Ssam 624189251Ssam /* User-Name */ 625189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 626189251Ssam identity, identity_len); 627189251Ssam 628189251Ssam /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts 629189251Ssam * the data, so no separate encryption is used in the AVP itself. 630189251Ssam * However, the password is padded to obfuscate its length. */ 631209158Srpaulo pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; 632189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, 633189251Ssam password_len + pad); 634189251Ssam os_memcpy(pos, password, password_len); 635189251Ssam pos += password_len; 636189251Ssam os_memset(pos, 0, pad); 637189251Ssam pos += pad; 638189251Ssam AVP_PAD(buf, pos); 639189251Ssam 640189251Ssam wpabuf_put(msg, pos - buf); 641189251Ssam *resp = msg; 642189251Ssam 643252726Srpaulo /* EAP-TTLS/PAP does not provide tunneled success notification, 644252726Srpaulo * so assume that Phase2 succeeds. */ 645252726Srpaulo ret->methodState = METHOD_DONE; 646252726Srpaulo ret->decision = DECISION_COND_SUCC; 647189251Ssam 648189251Ssam return 0; 649189251Ssam} 650189251Ssam 651189251Ssam 652189251Ssamstatic int eap_ttls_phase2_request_chap(struct eap_sm *sm, 653189251Ssam struct eap_ttls_data *data, 654189251Ssam struct eap_method_ret *ret, 655189251Ssam struct wpabuf **resp) 656189251Ssam{ 657189251Ssam struct wpabuf *msg; 658189251Ssam u8 *buf, *pos, *challenge; 659189251Ssam const u8 *identity, *password; 660189251Ssam size_t identity_len, password_len; 661189251Ssam 662189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); 663189251Ssam 664189251Ssam identity = eap_get_config_identity(sm, &identity_len); 665189251Ssam password = eap_get_config_password(sm, &password_len); 666189251Ssam if (identity == NULL || password == NULL) 667189251Ssam return -1; 668189251Ssam 669189251Ssam msg = wpabuf_alloc(identity_len + 1000); 670189251Ssam if (msg == NULL) { 671189251Ssam wpa_printf(MSG_ERROR, 672189251Ssam "EAP-TTLS/CHAP: Failed to allocate memory"); 673189251Ssam return -1; 674189251Ssam } 675189251Ssam pos = buf = wpabuf_mhead(msg); 676189251Ssam 677189251Ssam /* User-Name */ 678189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, 679189251Ssam identity, identity_len); 680189251Ssam 681189251Ssam /* CHAP-Challenge */ 682189251Ssam challenge = eap_ttls_implicit_challenge( 683189251Ssam sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); 684189251Ssam if (challenge == NULL) { 685189251Ssam wpabuf_free(msg); 686189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " 687189251Ssam "implicit challenge"); 688189251Ssam return -1; 689189251Ssam } 690189251Ssam 691189251Ssam pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, 692189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 693189251Ssam 694189251Ssam /* CHAP-Password */ 695189251Ssam pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, 696189251Ssam 1 + EAP_TTLS_CHAP_PASSWORD_LEN); 697189251Ssam data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; 698189251Ssam *pos++ = data->ident; 699189251Ssam 700189251Ssam /* MD5(Ident + Password + Challenge) */ 701189251Ssam chap_md5(data->ident, password, password_len, challenge, 702189251Ssam EAP_TTLS_CHAP_CHALLENGE_LEN, pos); 703189251Ssam 704189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", 705189251Ssam identity, identity_len); 706189251Ssam wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", 707189251Ssam password, password_len); 708189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", 709189251Ssam challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); 710189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", 711189251Ssam pos, EAP_TTLS_CHAP_PASSWORD_LEN); 712189251Ssam pos += EAP_TTLS_CHAP_PASSWORD_LEN; 713189251Ssam os_free(challenge); 714189251Ssam AVP_PAD(buf, pos); 715189251Ssam 716189251Ssam wpabuf_put(msg, pos - buf); 717189251Ssam *resp = msg; 718189251Ssam 719252726Srpaulo /* EAP-TTLS/CHAP does not provide tunneled success 720252726Srpaulo * notification, so assume that Phase2 succeeds. */ 721252726Srpaulo ret->methodState = METHOD_DONE; 722252726Srpaulo ret->decision = DECISION_COND_SUCC; 723189251Ssam 724189251Ssam return 0; 725189251Ssam} 726189251Ssam 727189251Ssam 728189251Ssamstatic int eap_ttls_phase2_request(struct eap_sm *sm, 729189251Ssam struct eap_ttls_data *data, 730189251Ssam struct eap_method_ret *ret, 731189251Ssam struct eap_hdr *hdr, 732189251Ssam struct wpabuf **resp) 733189251Ssam{ 734189251Ssam int res = 0; 735189251Ssam size_t len; 736189251Ssam enum phase2_types phase2_type = data->phase2_type; 737189251Ssam 738189251Ssam#ifdef EAP_TNC 739189251Ssam if (data->tnc_started) { 740189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); 741189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 742189251Ssam } 743189251Ssam#endif /* EAP_TNC */ 744189251Ssam 745189251Ssam if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || 746189251Ssam phase2_type == EAP_TTLS_PHASE2_MSCHAP || 747189251Ssam phase2_type == EAP_TTLS_PHASE2_PAP || 748189251Ssam phase2_type == EAP_TTLS_PHASE2_CHAP) { 749189251Ssam if (eap_get_config_identity(sm, &len) == NULL) { 750189251Ssam wpa_printf(MSG_INFO, 751189251Ssam "EAP-TTLS: Identity not configured"); 752189251Ssam eap_sm_request_identity(sm); 753189251Ssam if (eap_get_config_password(sm, &len) == NULL) 754189251Ssam eap_sm_request_password(sm); 755189251Ssam return 0; 756189251Ssam } 757189251Ssam 758189251Ssam if (eap_get_config_password(sm, &len) == NULL) { 759189251Ssam wpa_printf(MSG_INFO, 760189251Ssam "EAP-TTLS: Password not configured"); 761189251Ssam eap_sm_request_password(sm); 762189251Ssam return 0; 763189251Ssam } 764189251Ssam } 765189251Ssam 766189251Ssam switch (phase2_type) { 767189251Ssam case EAP_TTLS_PHASE2_EAP: 768189251Ssam res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); 769189251Ssam break; 770189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 771189251Ssam res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); 772189251Ssam break; 773189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 774189251Ssam res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); 775189251Ssam break; 776189251Ssam case EAP_TTLS_PHASE2_PAP: 777189251Ssam res = eap_ttls_phase2_request_pap(sm, data, ret, resp); 778189251Ssam break; 779189251Ssam case EAP_TTLS_PHASE2_CHAP: 780189251Ssam res = eap_ttls_phase2_request_chap(sm, data, ret, resp); 781189251Ssam break; 782189251Ssam default: 783189251Ssam wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); 784189251Ssam res = -1; 785189251Ssam break; 786189251Ssam } 787189251Ssam 788189251Ssam if (res < 0) { 789189251Ssam ret->methodState = METHOD_DONE; 790189251Ssam ret->decision = DECISION_FAIL; 791189251Ssam } 792189251Ssam 793189251Ssam return res; 794189251Ssam} 795189251Ssam 796189251Ssam 797189251Ssamstruct ttls_parse_avp { 798189251Ssam u8 *mschapv2; 799189251Ssam u8 *eapdata; 800189251Ssam size_t eap_len; 801189251Ssam int mschapv2_error; 802189251Ssam}; 803189251Ssam 804189251Ssam 805189251Ssamstatic int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, 806189251Ssam struct ttls_parse_avp *parse) 807189251Ssam{ 808189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); 809189251Ssam if (parse->eapdata == NULL) { 810189251Ssam parse->eapdata = os_malloc(dlen); 811189251Ssam if (parse->eapdata == NULL) { 812189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 813189251Ssam "memory for Phase 2 EAP data"); 814189251Ssam return -1; 815189251Ssam } 816189251Ssam os_memcpy(parse->eapdata, dpos, dlen); 817189251Ssam parse->eap_len = dlen; 818189251Ssam } else { 819189251Ssam u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); 820189251Ssam if (neweap == NULL) { 821189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " 822189251Ssam "memory for Phase 2 EAP data"); 823189251Ssam return -1; 824189251Ssam } 825189251Ssam os_memcpy(neweap + parse->eap_len, dpos, dlen); 826189251Ssam parse->eapdata = neweap; 827189251Ssam parse->eap_len += dlen; 828189251Ssam } 829189251Ssam 830189251Ssam return 0; 831189251Ssam} 832189251Ssam 833189251Ssam 834189251Ssamstatic int eap_ttls_parse_avp(u8 *pos, size_t left, 835189251Ssam struct ttls_parse_avp *parse) 836189251Ssam{ 837189251Ssam struct ttls_avp *avp; 838189251Ssam u32 avp_code, avp_length, vendor_id = 0; 839189251Ssam u8 avp_flags, *dpos; 840189251Ssam size_t dlen; 841189251Ssam 842189251Ssam avp = (struct ttls_avp *) pos; 843189251Ssam avp_code = be_to_host32(avp->avp_code); 844189251Ssam avp_length = be_to_host32(avp->avp_length); 845189251Ssam avp_flags = (avp_length >> 24) & 0xff; 846189251Ssam avp_length &= 0xffffff; 847189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " 848189251Ssam "length=%d", (int) avp_code, avp_flags, 849189251Ssam (int) avp_length); 850189251Ssam 851189251Ssam if (avp_length > left) { 852189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " 853189251Ssam "(len=%d, left=%lu) - dropped", 854189251Ssam (int) avp_length, (unsigned long) left); 855189251Ssam return -1; 856189251Ssam } 857189251Ssam 858189251Ssam if (avp_length < sizeof(*avp)) { 859189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", 860189251Ssam avp_length); 861189251Ssam return -1; 862189251Ssam } 863189251Ssam 864189251Ssam dpos = (u8 *) (avp + 1); 865189251Ssam dlen = avp_length - sizeof(*avp); 866189251Ssam if (avp_flags & AVP_FLAGS_VENDOR) { 867189251Ssam if (dlen < 4) { 868189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " 869189251Ssam "underflow"); 870189251Ssam return -1; 871189251Ssam } 872189251Ssam vendor_id = WPA_GET_BE32(dpos); 873189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", 874189251Ssam (int) vendor_id); 875189251Ssam dpos += 4; 876189251Ssam dlen -= 4; 877189251Ssam } 878189251Ssam 879189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); 880189251Ssam 881189251Ssam if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { 882189251Ssam if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) 883189251Ssam return -1; 884189251Ssam } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { 885189251Ssam /* This is an optional message that can be displayed to 886189251Ssam * the user. */ 887189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", 888189251Ssam dpos, dlen); 889189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 890189251Ssam avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { 891189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", 892189251Ssam dpos, dlen); 893189251Ssam if (dlen != 43) { 894189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " 895189251Ssam "MS-CHAP2-Success length " 896189251Ssam "(len=%lu, expected 43)", 897189251Ssam (unsigned long) dlen); 898189251Ssam return -1; 899189251Ssam } 900189251Ssam parse->mschapv2 = dpos; 901189251Ssam } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && 902189251Ssam avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { 903189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", 904189251Ssam dpos, dlen); 905189251Ssam parse->mschapv2_error = 1; 906189251Ssam } else if (avp_flags & AVP_FLAGS_MANDATORY) { 907189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " 908189251Ssam "code %d vendor_id %d - dropped", 909189251Ssam (int) avp_code, (int) vendor_id); 910189251Ssam return -1; 911189251Ssam } else { 912189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " 913189251Ssam "code %d vendor_id %d", 914189251Ssam (int) avp_code, (int) vendor_id); 915189251Ssam } 916189251Ssam 917189251Ssam return avp_length; 918189251Ssam} 919189251Ssam 920189251Ssam 921189251Ssamstatic int eap_ttls_parse_avps(struct wpabuf *in_decrypted, 922189251Ssam struct ttls_parse_avp *parse) 923189251Ssam{ 924189251Ssam u8 *pos; 925189251Ssam size_t left, pad; 926189251Ssam int avp_length; 927189251Ssam 928189251Ssam pos = wpabuf_mhead(in_decrypted); 929189251Ssam left = wpabuf_len(in_decrypted); 930189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); 931189251Ssam if (left < sizeof(struct ttls_avp)) { 932189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" 933189251Ssam " len=%lu expected %lu or more - dropped", 934189251Ssam (unsigned long) left, 935189251Ssam (unsigned long) sizeof(struct ttls_avp)); 936189251Ssam return -1; 937189251Ssam } 938189251Ssam 939189251Ssam /* Parse AVPs */ 940189251Ssam os_memset(parse, 0, sizeof(*parse)); 941189251Ssam 942189251Ssam while (left > 0) { 943189251Ssam avp_length = eap_ttls_parse_avp(pos, left, parse); 944189251Ssam if (avp_length < 0) 945189251Ssam return -1; 946189251Ssam 947189251Ssam pad = (4 - (avp_length & 3)) & 3; 948189251Ssam pos += avp_length + pad; 949189251Ssam if (left < avp_length + pad) 950189251Ssam left = 0; 951189251Ssam else 952189251Ssam left -= avp_length + pad; 953189251Ssam } 954189251Ssam 955189251Ssam return 0; 956189251Ssam} 957189251Ssam 958189251Ssam 959189251Ssamstatic u8 * eap_ttls_fake_identity_request(void) 960189251Ssam{ 961189251Ssam struct eap_hdr *hdr; 962189251Ssam u8 *buf; 963189251Ssam 964189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " 965189251Ssam "Phase 2 - use fake EAP-Request Identity"); 966189251Ssam buf = os_malloc(sizeof(*hdr) + 1); 967189251Ssam if (buf == NULL) { 968189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " 969189251Ssam "memory for fake EAP-Identity Request"); 970189251Ssam return NULL; 971189251Ssam } 972189251Ssam 973189251Ssam hdr = (struct eap_hdr *) buf; 974189251Ssam hdr->code = EAP_CODE_REQUEST; 975189251Ssam hdr->identifier = 0; 976189251Ssam hdr->length = host_to_be16(sizeof(*hdr) + 1); 977189251Ssam buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; 978189251Ssam 979189251Ssam return buf; 980189251Ssam} 981189251Ssam 982189251Ssam 983189251Ssamstatic int eap_ttls_encrypt_response(struct eap_sm *sm, 984189251Ssam struct eap_ttls_data *data, 985189251Ssam struct wpabuf *resp, u8 identifier, 986189251Ssam struct wpabuf **out_data) 987189251Ssam{ 988189251Ssam if (resp == NULL) 989189251Ssam return 0; 990189251Ssam 991189251Ssam wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", 992189251Ssam resp); 993189251Ssam if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 994189251Ssam data->ttls_version, identifier, 995189251Ssam resp, out_data)) { 996189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " 997189251Ssam "frame"); 998281806Srpaulo wpabuf_free(resp); 999189251Ssam return -1; 1000189251Ssam } 1001189251Ssam wpabuf_free(resp); 1002189251Ssam 1003189251Ssam return 0; 1004189251Ssam} 1005189251Ssam 1006189251Ssam 1007189251Ssamstatic int eap_ttls_process_phase2_eap(struct eap_sm *sm, 1008189251Ssam struct eap_ttls_data *data, 1009189251Ssam struct eap_method_ret *ret, 1010189251Ssam struct ttls_parse_avp *parse, 1011189251Ssam struct wpabuf **resp) 1012189251Ssam{ 1013189251Ssam struct eap_hdr *hdr; 1014189251Ssam size_t len; 1015189251Ssam 1016189251Ssam if (parse->eapdata == NULL) { 1017189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " 1018189251Ssam "packet - dropped"); 1019189251Ssam return -1; 1020189251Ssam } 1021189251Ssam 1022189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", 1023189251Ssam parse->eapdata, parse->eap_len); 1024189251Ssam hdr = (struct eap_hdr *) parse->eapdata; 1025189251Ssam 1026189251Ssam if (parse->eap_len < sizeof(*hdr)) { 1027189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " 1028189251Ssam "frame (len=%lu, expected %lu or more) - dropped", 1029189251Ssam (unsigned long) parse->eap_len, 1030189251Ssam (unsigned long) sizeof(*hdr)); 1031189251Ssam return -1; 1032189251Ssam } 1033189251Ssam len = be_to_host16(hdr->length); 1034189251Ssam if (len > parse->eap_len) { 1035189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " 1036189251Ssam "EAP frame (EAP hdr len=%lu, EAP data len in " 1037189251Ssam "AVP=%lu)", 1038189251Ssam (unsigned long) len, 1039189251Ssam (unsigned long) parse->eap_len); 1040189251Ssam return -1; 1041189251Ssam } 1042189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " 1043189251Ssam "identifier=%d length=%lu", 1044189251Ssam hdr->code, hdr->identifier, (unsigned long) len); 1045189251Ssam switch (hdr->code) { 1046189251Ssam case EAP_CODE_REQUEST: 1047189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { 1048189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1049189251Ssam "processing failed"); 1050189251Ssam return -1; 1051189251Ssam } 1052189251Ssam break; 1053189251Ssam default: 1054189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " 1055189251Ssam "Phase 2 EAP header", hdr->code); 1056189251Ssam return -1; 1057189251Ssam } 1058189251Ssam 1059189251Ssam return 0; 1060189251Ssam} 1061189251Ssam 1062189251Ssam 1063189251Ssamstatic int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, 1064189251Ssam struct eap_ttls_data *data, 1065189251Ssam struct eap_method_ret *ret, 1066189251Ssam struct ttls_parse_avp *parse) 1067189251Ssam{ 1068252726Srpaulo#ifdef EAP_MSCHAPv2 1069189251Ssam if (parse->mschapv2_error) { 1070189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " 1071189251Ssam "MS-CHAP-Error - failed"); 1072189251Ssam ret->methodState = METHOD_DONE; 1073189251Ssam ret->decision = DECISION_FAIL; 1074189251Ssam /* Reply with empty data to ACK error */ 1075189251Ssam return 1; 1076189251Ssam } 1077189251Ssam 1078189251Ssam if (parse->mschapv2 == NULL) { 1079189251Ssam#ifdef EAP_TNC 1080189251Ssam if (data->phase2_success && parse->eapdata) { 1081189251Ssam /* 1082189251Ssam * Allow EAP-TNC to be started after successfully 1083189251Ssam * completed MSCHAPV2. 1084189251Ssam */ 1085189251Ssam return 1; 1086189251Ssam } 1087189251Ssam#endif /* EAP_TNC */ 1088189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " 1089189251Ssam "received for Phase2 MSCHAPV2"); 1090189251Ssam return -1; 1091189251Ssam } 1092189251Ssam if (parse->mschapv2[0] != data->ident) { 1093189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " 1094189251Ssam "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", 1095189251Ssam parse->mschapv2[0], data->ident); 1096189251Ssam return -1; 1097189251Ssam } 1098189251Ssam if (!data->auth_response_valid || 1099189251Ssam mschapv2_verify_auth_response(data->auth_response, 1100189251Ssam parse->mschapv2 + 1, 42)) { 1101189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " 1102189251Ssam "response in Phase 2 MSCHAPV2 success request"); 1103189251Ssam return -1; 1104189251Ssam } 1105189251Ssam 1106189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " 1107189251Ssam "authentication succeeded"); 1108252726Srpaulo ret->methodState = METHOD_DONE; 1109252726Srpaulo ret->decision = DECISION_UNCOND_SUCC; 1110252726Srpaulo data->phase2_success = 1; 1111189251Ssam 1112189251Ssam /* 1113189251Ssam * Reply with empty data; authentication server will reply 1114189251Ssam * with EAP-Success after this. 1115189251Ssam */ 1116189251Ssam return 1; 1117252726Srpaulo#else /* EAP_MSCHAPv2 */ 1118252726Srpaulo wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); 1119252726Srpaulo return -1; 1120252726Srpaulo#endif /* EAP_MSCHAPv2 */ 1121189251Ssam} 1122189251Ssam 1123189251Ssam 1124189251Ssam#ifdef EAP_TNC 1125189251Ssamstatic int eap_ttls_process_tnc_start(struct eap_sm *sm, 1126189251Ssam struct eap_ttls_data *data, 1127189251Ssam struct eap_method_ret *ret, 1128189251Ssam struct ttls_parse_avp *parse, 1129189251Ssam struct wpabuf **resp) 1130189251Ssam{ 1131189251Ssam /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ 1132189251Ssam if (parse->eapdata == NULL) { 1133189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1134189251Ssam "unexpected tunneled data (no EAP)"); 1135189251Ssam return -1; 1136189251Ssam } 1137189251Ssam 1138189251Ssam if (!data->ready_for_tnc) { 1139189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " 1140189251Ssam "EAP after non-EAP, but not ready for TNC"); 1141189251Ssam return -1; 1142189251Ssam } 1143189251Ssam 1144189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " 1145189251Ssam "non-EAP method"); 1146189251Ssam data->tnc_started = 1; 1147189251Ssam 1148189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) 1149189251Ssam return -1; 1150189251Ssam 1151189251Ssam return 0; 1152189251Ssam} 1153189251Ssam#endif /* EAP_TNC */ 1154189251Ssam 1155189251Ssam 1156189251Ssamstatic int eap_ttls_process_decrypted(struct eap_sm *sm, 1157189251Ssam struct eap_ttls_data *data, 1158189251Ssam struct eap_method_ret *ret, 1159189251Ssam u8 identifier, 1160189251Ssam struct ttls_parse_avp *parse, 1161189251Ssam struct wpabuf *in_decrypted, 1162189251Ssam struct wpabuf **out_data) 1163189251Ssam{ 1164189251Ssam struct wpabuf *resp = NULL; 1165189251Ssam struct eap_peer_config *config = eap_get_config(sm); 1166189251Ssam int res; 1167189251Ssam enum phase2_types phase2_type = data->phase2_type; 1168189251Ssam 1169189251Ssam#ifdef EAP_TNC 1170189251Ssam if (data->tnc_started) 1171189251Ssam phase2_type = EAP_TTLS_PHASE2_EAP; 1172189251Ssam#endif /* EAP_TNC */ 1173189251Ssam 1174189251Ssam switch (phase2_type) { 1175189251Ssam case EAP_TTLS_PHASE2_EAP: 1176189251Ssam if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < 1177189251Ssam 0) 1178189251Ssam return -1; 1179189251Ssam break; 1180189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1181189251Ssam res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); 1182189251Ssam#ifdef EAP_TNC 1183189251Ssam if (res == 1 && parse->eapdata && data->phase2_success) { 1184189251Ssam /* 1185189251Ssam * TNC may be required as the next 1186189251Ssam * authentication method within the tunnel. 1187189251Ssam */ 1188189251Ssam ret->methodState = METHOD_MAY_CONT; 1189189251Ssam data->ready_for_tnc = 1; 1190189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, 1191189251Ssam &resp) == 0) 1192189251Ssam break; 1193189251Ssam } 1194189251Ssam#endif /* EAP_TNC */ 1195189251Ssam return res; 1196189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1197189251Ssam case EAP_TTLS_PHASE2_PAP: 1198189251Ssam case EAP_TTLS_PHASE2_CHAP: 1199189251Ssam#ifdef EAP_TNC 1200189251Ssam if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < 1201189251Ssam 0) 1202189251Ssam return -1; 1203189251Ssam break; 1204189251Ssam#else /* EAP_TNC */ 1205189251Ssam /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled 1206189251Ssam * requests to the supplicant */ 1207189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " 1208189251Ssam "tunneled data"); 1209189251Ssam return -1; 1210189251Ssam#endif /* EAP_TNC */ 1211189251Ssam } 1212189251Ssam 1213189251Ssam if (resp) { 1214189251Ssam if (eap_ttls_encrypt_response(sm, data, resp, identifier, 1215189251Ssam out_data) < 0) 1216189251Ssam return -1; 1217189251Ssam } else if (config->pending_req_identity || 1218189251Ssam config->pending_req_password || 1219189251Ssam config->pending_req_otp || 1220189251Ssam config->pending_req_new_password) { 1221189251Ssam wpabuf_free(data->pending_phase2_req); 1222189251Ssam data->pending_phase2_req = wpabuf_dup(in_decrypted); 1223189251Ssam } 1224189251Ssam 1225189251Ssam return 0; 1226189251Ssam} 1227189251Ssam 1228189251Ssam 1229189251Ssamstatic int eap_ttls_implicit_identity_request(struct eap_sm *sm, 1230189251Ssam struct eap_ttls_data *data, 1231189251Ssam struct eap_method_ret *ret, 1232189251Ssam u8 identifier, 1233189251Ssam struct wpabuf **out_data) 1234189251Ssam{ 1235189251Ssam int retval = 0; 1236189251Ssam struct eap_hdr *hdr; 1237189251Ssam struct wpabuf *resp; 1238189251Ssam 1239189251Ssam hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); 1240189251Ssam if (hdr == NULL) { 1241189251Ssam ret->methodState = METHOD_DONE; 1242189251Ssam ret->decision = DECISION_FAIL; 1243189251Ssam return -1; 1244189251Ssam } 1245189251Ssam 1246189251Ssam resp = NULL; 1247189251Ssam if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { 1248189251Ssam wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " 1249189251Ssam "processing failed"); 1250189251Ssam retval = -1; 1251189251Ssam } else { 1252252726Srpaulo struct eap_peer_config *config = eap_get_config(sm); 1253252726Srpaulo if (resp == NULL && 1254252726Srpaulo (config->pending_req_identity || 1255252726Srpaulo config->pending_req_password || 1256252726Srpaulo config->pending_req_otp || 1257252726Srpaulo config->pending_req_new_password)) { 1258252726Srpaulo /* 1259252726Srpaulo * Use empty buffer to force implicit request 1260252726Srpaulo * processing when EAP request is re-processed after 1261252726Srpaulo * user input. 1262252726Srpaulo */ 1263252726Srpaulo wpabuf_free(data->pending_phase2_req); 1264252726Srpaulo data->pending_phase2_req = wpabuf_alloc(0); 1265252726Srpaulo } 1266252726Srpaulo 1267189251Ssam retval = eap_ttls_encrypt_response(sm, data, resp, identifier, 1268189251Ssam out_data); 1269189251Ssam } 1270189251Ssam 1271189251Ssam os_free(hdr); 1272189251Ssam 1273189251Ssam if (retval < 0) { 1274189251Ssam ret->methodState = METHOD_DONE; 1275189251Ssam ret->decision = DECISION_FAIL; 1276189251Ssam } 1277189251Ssam 1278189251Ssam return retval; 1279189251Ssam} 1280189251Ssam 1281189251Ssam 1282189251Ssamstatic int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, 1283189251Ssam struct eap_method_ret *ret, u8 identifier, 1284189251Ssam struct wpabuf **out_data) 1285189251Ssam{ 1286189251Ssam data->phase2_start = 0; 1287189251Ssam 1288189251Ssam /* 1289189251Ssam * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only 1290189251Ssam * if TLS part was indeed resuming a previous session. Most 1291189251Ssam * Authentication Servers terminate EAP-TTLS before reaching this 1292189251Ssam * point, but some do not. Make wpa_supplicant stop phase 2 here, if 1293189251Ssam * needed. 1294189251Ssam */ 1295189251Ssam if (data->reauth && 1296189251Ssam tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { 1297189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " 1298189251Ssam "skip phase 2"); 1299189251Ssam *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, 1300189251Ssam data->ttls_version); 1301189251Ssam ret->methodState = METHOD_DONE; 1302189251Ssam ret->decision = DECISION_UNCOND_SUCC; 1303189251Ssam data->phase2_success = 1; 1304189251Ssam return 0; 1305189251Ssam } 1306189251Ssam 1307189251Ssam return eap_ttls_implicit_identity_request(sm, data, ret, identifier, 1308189251Ssam out_data); 1309189251Ssam} 1310189251Ssam 1311189251Ssam 1312189251Ssamstatic int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, 1313189251Ssam struct eap_method_ret *ret, u8 identifier, 1314189251Ssam const struct wpabuf *in_data, 1315189251Ssam struct wpabuf **out_data) 1316189251Ssam{ 1317189251Ssam struct wpabuf *in_decrypted = NULL; 1318189251Ssam int retval = 0; 1319189251Ssam struct ttls_parse_avp parse; 1320189251Ssam 1321189251Ssam os_memset(&parse, 0, sizeof(parse)); 1322189251Ssam 1323189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" 1324189251Ssam " Phase 2", 1325189251Ssam in_data ? (unsigned long) wpabuf_len(in_data) : 0); 1326189251Ssam 1327189251Ssam if (data->pending_phase2_req) { 1328189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " 1329189251Ssam "skip decryption and use old data"); 1330189251Ssam /* Clear TLS reassembly state. */ 1331189251Ssam eap_peer_tls_reset_input(&data->ssl); 1332189251Ssam 1333189251Ssam in_decrypted = data->pending_phase2_req; 1334189251Ssam data->pending_phase2_req = NULL; 1335189251Ssam if (wpabuf_len(in_decrypted) == 0) { 1336189251Ssam wpabuf_free(in_decrypted); 1337189251Ssam return eap_ttls_implicit_identity_request( 1338189251Ssam sm, data, ret, identifier, out_data); 1339189251Ssam } 1340189251Ssam goto continue_req; 1341189251Ssam } 1342189251Ssam 1343189251Ssam if ((in_data == NULL || wpabuf_len(in_data) == 0) && 1344189251Ssam data->phase2_start) { 1345189251Ssam return eap_ttls_phase2_start(sm, data, ret, identifier, 1346189251Ssam out_data); 1347189251Ssam } 1348189251Ssam 1349189251Ssam if (in_data == NULL || wpabuf_len(in_data) == 0) { 1350189251Ssam /* Received TLS ACK - requesting more fragments */ 1351189251Ssam return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, 1352189251Ssam data->ttls_version, 1353189251Ssam identifier, NULL, out_data); 1354189251Ssam } 1355189251Ssam 1356189251Ssam retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); 1357189251Ssam if (retval) 1358189251Ssam goto done; 1359189251Ssam 1360189251Ssamcontinue_req: 1361189251Ssam data->phase2_start = 0; 1362189251Ssam 1363189251Ssam if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { 1364189251Ssam retval = -1; 1365189251Ssam goto done; 1366189251Ssam } 1367189251Ssam 1368189251Ssam retval = eap_ttls_process_decrypted(sm, data, ret, identifier, 1369189251Ssam &parse, in_decrypted, out_data); 1370189251Ssam 1371189251Ssamdone: 1372189251Ssam wpabuf_free(in_decrypted); 1373189251Ssam os_free(parse.eapdata); 1374189251Ssam 1375189251Ssam if (retval < 0) { 1376189251Ssam ret->methodState = METHOD_DONE; 1377189251Ssam ret->decision = DECISION_FAIL; 1378189251Ssam } 1379189251Ssam 1380189251Ssam return retval; 1381189251Ssam} 1382189251Ssam 1383189251Ssam 1384189251Ssamstatic int eap_ttls_process_handshake(struct eap_sm *sm, 1385189251Ssam struct eap_ttls_data *data, 1386189251Ssam struct eap_method_ret *ret, 1387189251Ssam u8 identifier, 1388189251Ssam const u8 *in_data, size_t in_len, 1389189251Ssam struct wpabuf **out_data) 1390189251Ssam{ 1391189251Ssam int res; 1392189251Ssam 1393189251Ssam res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, 1394189251Ssam data->ttls_version, identifier, 1395189251Ssam in_data, in_len, out_data); 1396189251Ssam 1397189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { 1398189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " 1399189251Ssam "Phase 2"); 1400189251Ssam if (data->resuming) { 1401189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " 1402189251Ssam "skip Phase 2"); 1403189251Ssam ret->decision = DECISION_COND_SUCC; 1404189251Ssam ret->methodState = METHOD_MAY_CONT; 1405189251Ssam } 1406189251Ssam data->phase2_start = 1; 1407252726Srpaulo eap_ttls_v0_derive_key(sm, data); 1408189251Ssam 1409189251Ssam if (*out_data == NULL || wpabuf_len(*out_data) == 0) { 1410189251Ssam if (eap_ttls_decrypt(sm, data, ret, identifier, 1411189251Ssam NULL, out_data)) { 1412189251Ssam wpa_printf(MSG_WARNING, "EAP-TTLS: " 1413189251Ssam "failed to process early " 1414189251Ssam "start for Phase 2"); 1415189251Ssam } 1416189251Ssam res = 0; 1417189251Ssam } 1418189251Ssam data->resuming = 0; 1419189251Ssam } 1420189251Ssam 1421189251Ssam if (res == 2) { 1422189251Ssam struct wpabuf msg; 1423189251Ssam /* 1424189251Ssam * Application data included in the handshake message. 1425189251Ssam */ 1426189251Ssam wpabuf_free(data->pending_phase2_req); 1427189251Ssam data->pending_phase2_req = *out_data; 1428189251Ssam *out_data = NULL; 1429189251Ssam wpabuf_set(&msg, in_data, in_len); 1430189251Ssam res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, 1431189251Ssam out_data); 1432189251Ssam } 1433189251Ssam 1434189251Ssam return res; 1435189251Ssam} 1436189251Ssam 1437189251Ssam 1438189251Ssamstatic void eap_ttls_check_auth_status(struct eap_sm *sm, 1439189251Ssam struct eap_ttls_data *data, 1440189251Ssam struct eap_method_ret *ret) 1441189251Ssam{ 1442252726Srpaulo if (ret->methodState == METHOD_DONE) { 1443189251Ssam ret->allowNotifications = FALSE; 1444189251Ssam if (ret->decision == DECISION_UNCOND_SUCC || 1445189251Ssam ret->decision == DECISION_COND_SUCC) { 1446189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1447189251Ssam "completed successfully"); 1448189251Ssam data->phase2_success = 1; 1449189251Ssam#ifdef EAP_TNC 1450189251Ssam if (!data->ready_for_tnc && !data->tnc_started) { 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 } 1458189251Ssam#endif /* EAP_TNC */ 1459189251Ssam } 1460252726Srpaulo } else if (ret->methodState == METHOD_MAY_CONT && 1461189251Ssam (ret->decision == DECISION_UNCOND_SUCC || 1462189251Ssam ret->decision == DECISION_COND_SUCC)) { 1463189251Ssam wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " 1464189251Ssam "completed successfully (MAY_CONT)"); 1465189251Ssam data->phase2_success = 1; 1466189251Ssam } 1467189251Ssam} 1468189251Ssam 1469189251Ssam 1470189251Ssamstatic struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, 1471189251Ssam struct eap_method_ret *ret, 1472189251Ssam const struct wpabuf *reqData) 1473189251Ssam{ 1474189251Ssam size_t left; 1475189251Ssam int res; 1476189251Ssam u8 flags, id; 1477189251Ssam struct wpabuf *resp; 1478189251Ssam const u8 *pos; 1479189251Ssam struct eap_ttls_data *data = priv; 1480189251Ssam 1481189251Ssam pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, 1482189251Ssam reqData, &left, &flags); 1483189251Ssam if (pos == NULL) 1484189251Ssam return NULL; 1485189251Ssam id = eap_get_id(reqData); 1486189251Ssam 1487189251Ssam if (flags & EAP_TLS_FLAGS_START) { 1488252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " 1489252726Srpaulo "ver=%d)", flags & EAP_TLS_VERSION_MASK, 1490252726Srpaulo data->ttls_version); 1491189251Ssam 1492189251Ssam /* RFC 5281, Ch. 9.2: 1493189251Ssam * "This packet MAY contain additional information in the form 1494189251Ssam * of AVPs, which may provide useful hints to the client" 1495189251Ssam * For now, ignore any potential extra data. 1496189251Ssam */ 1497189251Ssam left = 0; 1498189251Ssam } 1499189251Ssam 1500189251Ssam resp = NULL; 1501189251Ssam if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1502189251Ssam !data->resuming) { 1503189251Ssam struct wpabuf msg; 1504189251Ssam wpabuf_set(&msg, pos, left); 1505189251Ssam res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); 1506189251Ssam } else { 1507189251Ssam res = eap_ttls_process_handshake(sm, data, ret, id, 1508189251Ssam pos, left, &resp); 1509189251Ssam } 1510189251Ssam 1511189251Ssam eap_ttls_check_auth_status(sm, data, ret); 1512189251Ssam 1513189251Ssam /* FIX: what about res == -1? Could just move all error processing into 1514189251Ssam * the other functions and get rid of this res==1 case here. */ 1515189251Ssam if (res == 1) { 1516189251Ssam wpabuf_free(resp); 1517189251Ssam return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, 1518189251Ssam data->ttls_version); 1519189251Ssam } 1520189251Ssam return resp; 1521189251Ssam} 1522189251Ssam 1523189251Ssam 1524189251Ssamstatic Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) 1525189251Ssam{ 1526189251Ssam struct eap_ttls_data *data = priv; 1527189251Ssam return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 1528189251Ssam data->phase2_success; 1529189251Ssam} 1530189251Ssam 1531189251Ssam 1532189251Ssamstatic void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) 1533189251Ssam{ 1534189251Ssam struct eap_ttls_data *data = priv; 1535189251Ssam wpabuf_free(data->pending_phase2_req); 1536189251Ssam data->pending_phase2_req = NULL; 1537189251Ssam#ifdef EAP_TNC 1538189251Ssam data->ready_for_tnc = 0; 1539189251Ssam data->tnc_started = 0; 1540189251Ssam#endif /* EAP_TNC */ 1541189251Ssam} 1542189251Ssam 1543189251Ssam 1544189251Ssamstatic void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) 1545189251Ssam{ 1546189251Ssam struct eap_ttls_data *data = priv; 1547281806Srpaulo eap_ttls_free_key(data); 1548281806Srpaulo os_free(data->session_id); 1549281806Srpaulo data->session_id = NULL; 1550189251Ssam if (eap_peer_tls_reauth_init(sm, &data->ssl)) { 1551189251Ssam os_free(data); 1552189251Ssam return NULL; 1553189251Ssam } 1554189251Ssam if (data->phase2_priv && data->phase2_method && 1555189251Ssam data->phase2_method->init_for_reauth) 1556189251Ssam data->phase2_method->init_for_reauth(sm, data->phase2_priv); 1557189251Ssam data->phase2_start = 0; 1558189251Ssam data->phase2_success = 0; 1559189251Ssam data->resuming = 1; 1560189251Ssam data->reauth = 1; 1561189251Ssam return priv; 1562189251Ssam} 1563189251Ssam 1564189251Ssam 1565189251Ssamstatic int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, 1566189251Ssam size_t buflen, int verbose) 1567189251Ssam{ 1568189251Ssam struct eap_ttls_data *data = priv; 1569189251Ssam int len, ret; 1570189251Ssam 1571189251Ssam len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); 1572189251Ssam ret = os_snprintf(buf + len, buflen - len, 1573189251Ssam "EAP-TTLSv%d Phase2 method=", 1574189251Ssam data->ttls_version); 1575281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 1576189251Ssam return len; 1577189251Ssam len += ret; 1578189251Ssam switch (data->phase2_type) { 1579189251Ssam case EAP_TTLS_PHASE2_EAP: 1580189251Ssam ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", 1581189251Ssam data->phase2_method ? 1582189251Ssam data->phase2_method->name : "?"); 1583189251Ssam break; 1584189251Ssam case EAP_TTLS_PHASE2_MSCHAPV2: 1585189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); 1586189251Ssam break; 1587189251Ssam case EAP_TTLS_PHASE2_MSCHAP: 1588189251Ssam ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); 1589189251Ssam break; 1590189251Ssam case EAP_TTLS_PHASE2_PAP: 1591189251Ssam ret = os_snprintf(buf + len, buflen - len, "PAP\n"); 1592189251Ssam break; 1593189251Ssam case EAP_TTLS_PHASE2_CHAP: 1594189251Ssam ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); 1595189251Ssam break; 1596189251Ssam default: 1597189251Ssam ret = 0; 1598189251Ssam break; 1599189251Ssam } 1600281806Srpaulo if (os_snprintf_error(buflen - len, ret)) 1601189251Ssam return len; 1602189251Ssam len += ret; 1603189251Ssam 1604189251Ssam return len; 1605189251Ssam} 1606189251Ssam 1607189251Ssam 1608189251Ssamstatic Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) 1609189251Ssam{ 1610189251Ssam struct eap_ttls_data *data = priv; 1611189251Ssam return data->key_data != NULL && data->phase2_success; 1612189251Ssam} 1613189251Ssam 1614189251Ssam 1615189251Ssamstatic u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) 1616189251Ssam{ 1617189251Ssam struct eap_ttls_data *data = priv; 1618189251Ssam u8 *key; 1619189251Ssam 1620189251Ssam if (data->key_data == NULL || !data->phase2_success) 1621189251Ssam return NULL; 1622189251Ssam 1623189251Ssam key = os_malloc(EAP_TLS_KEY_LEN); 1624189251Ssam if (key == NULL) 1625189251Ssam return NULL; 1626189251Ssam 1627189251Ssam *len = EAP_TLS_KEY_LEN; 1628189251Ssam os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); 1629189251Ssam 1630189251Ssam return key; 1631189251Ssam} 1632189251Ssam 1633189251Ssam 1634281806Srpaulostatic u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 1635281806Srpaulo{ 1636281806Srpaulo struct eap_ttls_data *data = priv; 1637281806Srpaulo u8 *id; 1638281806Srpaulo 1639281806Srpaulo if (data->session_id == NULL || !data->phase2_success) 1640281806Srpaulo return NULL; 1641281806Srpaulo 1642281806Srpaulo id = os_malloc(data->id_len); 1643281806Srpaulo if (id == NULL) 1644281806Srpaulo return NULL; 1645281806Srpaulo 1646281806Srpaulo *len = data->id_len; 1647281806Srpaulo os_memcpy(id, data->session_id, data->id_len); 1648281806Srpaulo 1649281806Srpaulo return id; 1650281806Srpaulo} 1651281806Srpaulo 1652281806Srpaulo 1653281806Srpaulostatic u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1654281806Srpaulo{ 1655281806Srpaulo struct eap_ttls_data *data = priv; 1656281806Srpaulo u8 *key; 1657281806Srpaulo 1658281806Srpaulo if (data->key_data == NULL) 1659281806Srpaulo return NULL; 1660281806Srpaulo 1661281806Srpaulo key = os_malloc(EAP_EMSK_LEN); 1662281806Srpaulo if (key == NULL) 1663281806Srpaulo return NULL; 1664281806Srpaulo 1665281806Srpaulo *len = EAP_EMSK_LEN; 1666281806Srpaulo os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); 1667281806Srpaulo 1668281806Srpaulo return key; 1669281806Srpaulo} 1670281806Srpaulo 1671281806Srpaulo 1672189251Ssamint eap_peer_ttls_register(void) 1673189251Ssam{ 1674189251Ssam struct eap_method *eap; 1675189251Ssam int ret; 1676189251Ssam 1677189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1678189251Ssam EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); 1679189251Ssam if (eap == NULL) 1680189251Ssam return -1; 1681189251Ssam 1682189251Ssam eap->init = eap_ttls_init; 1683189251Ssam eap->deinit = eap_ttls_deinit; 1684189251Ssam eap->process = eap_ttls_process; 1685189251Ssam eap->isKeyAvailable = eap_ttls_isKeyAvailable; 1686189251Ssam eap->getKey = eap_ttls_getKey; 1687281806Srpaulo eap->getSessionId = eap_ttls_get_session_id; 1688189251Ssam eap->get_status = eap_ttls_get_status; 1689189251Ssam eap->has_reauth_data = eap_ttls_has_reauth_data; 1690189251Ssam eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; 1691189251Ssam eap->init_for_reauth = eap_ttls_init_for_reauth; 1692281806Srpaulo eap->get_emsk = eap_ttls_get_emsk; 1693189251Ssam 1694189251Ssam ret = eap_peer_method_register(eap); 1695189251Ssam if (ret) 1696189251Ssam eap_peer_method_free(eap); 1697189251Ssam return ret; 1698189251Ssam} 1699