eap_server_tls.c revision 281806
1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-TLS (RFC 2716) 3214501Srpaulo * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12214501Srpaulo#include "eap_i.h" 13214501Srpaulo#include "eap_tls_common.h" 14214501Srpaulo#include "crypto/tls.h" 15214501Srpaulo 16214501Srpaulo 17214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv); 18214501Srpaulo 19214501Srpaulo 20214501Srpaulostruct eap_tls_data { 21214501Srpaulo struct eap_ssl_data ssl; 22214501Srpaulo enum { START, CONTINUE, SUCCESS, FAILURE } state; 23214501Srpaulo int established; 24252726Srpaulo u8 eap_type; 25214501Srpaulo}; 26214501Srpaulo 27214501Srpaulo 28214501Srpaulostatic const char * eap_tls_state_txt(int state) 29214501Srpaulo{ 30214501Srpaulo switch (state) { 31214501Srpaulo case START: 32214501Srpaulo return "START"; 33214501Srpaulo case CONTINUE: 34214501Srpaulo return "CONTINUE"; 35214501Srpaulo case SUCCESS: 36214501Srpaulo return "SUCCESS"; 37214501Srpaulo case FAILURE: 38214501Srpaulo return "FAILURE"; 39214501Srpaulo default: 40214501Srpaulo return "Unknown?!"; 41214501Srpaulo } 42214501Srpaulo} 43214501Srpaulo 44214501Srpaulo 45214501Srpaulostatic void eap_tls_state(struct eap_tls_data *data, int state) 46214501Srpaulo{ 47214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s", 48214501Srpaulo eap_tls_state_txt(data->state), 49214501Srpaulo eap_tls_state_txt(state)); 50214501Srpaulo data->state = state; 51214501Srpaulo} 52214501Srpaulo 53214501Srpaulo 54214501Srpaulostatic void * eap_tls_init(struct eap_sm *sm) 55214501Srpaulo{ 56214501Srpaulo struct eap_tls_data *data; 57214501Srpaulo 58214501Srpaulo data = os_zalloc(sizeof(*data)); 59214501Srpaulo if (data == NULL) 60214501Srpaulo return NULL; 61214501Srpaulo data->state = START; 62214501Srpaulo 63214501Srpaulo if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { 64214501Srpaulo wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); 65214501Srpaulo eap_tls_reset(sm, data); 66214501Srpaulo return NULL; 67214501Srpaulo } 68214501Srpaulo 69252726Srpaulo data->eap_type = EAP_TYPE_TLS; 70252726Srpaulo 71214501Srpaulo return data; 72214501Srpaulo} 73214501Srpaulo 74214501Srpaulo 75252726Srpaulo#ifdef EAP_SERVER_UNAUTH_TLS 76252726Srpaulostatic void * eap_unauth_tls_init(struct eap_sm *sm) 77252726Srpaulo{ 78252726Srpaulo struct eap_tls_data *data; 79252726Srpaulo 80252726Srpaulo data = os_zalloc(sizeof(*data)); 81252726Srpaulo if (data == NULL) 82252726Srpaulo return NULL; 83252726Srpaulo data->state = START; 84252726Srpaulo 85252726Srpaulo if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { 86252726Srpaulo wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); 87252726Srpaulo eap_tls_reset(sm, data); 88252726Srpaulo return NULL; 89252726Srpaulo } 90252726Srpaulo 91252726Srpaulo data->eap_type = EAP_UNAUTH_TLS_TYPE; 92252726Srpaulo return data; 93252726Srpaulo} 94252726Srpaulo#endif /* EAP_SERVER_UNAUTH_TLS */ 95252726Srpaulo 96252726Srpaulo 97281806Srpaulo#ifdef CONFIG_HS20 98281806Srpaulostatic void * eap_wfa_unauth_tls_init(struct eap_sm *sm) 99281806Srpaulo{ 100281806Srpaulo struct eap_tls_data *data; 101281806Srpaulo 102281806Srpaulo data = os_zalloc(sizeof(*data)); 103281806Srpaulo if (data == NULL) 104281806Srpaulo return NULL; 105281806Srpaulo data->state = START; 106281806Srpaulo 107281806Srpaulo if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { 108281806Srpaulo wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); 109281806Srpaulo eap_tls_reset(sm, data); 110281806Srpaulo return NULL; 111281806Srpaulo } 112281806Srpaulo 113281806Srpaulo data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE; 114281806Srpaulo return data; 115281806Srpaulo} 116281806Srpaulo#endif /* CONFIG_HS20 */ 117281806Srpaulo 118281806Srpaulo 119214501Srpaulostatic void eap_tls_reset(struct eap_sm *sm, void *priv) 120214501Srpaulo{ 121214501Srpaulo struct eap_tls_data *data = priv; 122214501Srpaulo if (data == NULL) 123214501Srpaulo return; 124214501Srpaulo eap_server_tls_ssl_deinit(sm, &data->ssl); 125214501Srpaulo os_free(data); 126214501Srpaulo} 127214501Srpaulo 128214501Srpaulo 129214501Srpaulostatic struct wpabuf * eap_tls_build_start(struct eap_sm *sm, 130214501Srpaulo struct eap_tls_data *data, u8 id) 131214501Srpaulo{ 132214501Srpaulo struct wpabuf *req; 133214501Srpaulo 134252726Srpaulo req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); 135214501Srpaulo if (req == NULL) { 136214501Srpaulo wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " 137214501Srpaulo "request"); 138214501Srpaulo eap_tls_state(data, FAILURE); 139214501Srpaulo return NULL; 140214501Srpaulo } 141214501Srpaulo 142214501Srpaulo wpabuf_put_u8(req, EAP_TLS_FLAGS_START); 143214501Srpaulo 144214501Srpaulo eap_tls_state(data, CONTINUE); 145214501Srpaulo 146214501Srpaulo return req; 147214501Srpaulo} 148214501Srpaulo 149214501Srpaulo 150214501Srpaulostatic struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) 151214501Srpaulo{ 152214501Srpaulo struct eap_tls_data *data = priv; 153214501Srpaulo struct wpabuf *res; 154214501Srpaulo 155214501Srpaulo if (data->ssl.state == FRAG_ACK) { 156252726Srpaulo return eap_server_tls_build_ack(id, data->eap_type, 0); 157214501Srpaulo } 158214501Srpaulo 159214501Srpaulo if (data->ssl.state == WAIT_FRAG_ACK) { 160252726Srpaulo res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, 161214501Srpaulo id); 162214501Srpaulo goto check_established; 163214501Srpaulo } 164214501Srpaulo 165214501Srpaulo switch (data->state) { 166214501Srpaulo case START: 167214501Srpaulo return eap_tls_build_start(sm, data, id); 168214501Srpaulo case CONTINUE: 169214501Srpaulo if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) 170214501Srpaulo data->established = 1; 171214501Srpaulo break; 172214501Srpaulo default: 173214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", 174214501Srpaulo __func__, data->state); 175214501Srpaulo return NULL; 176214501Srpaulo } 177214501Srpaulo 178252726Srpaulo res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); 179214501Srpaulo 180214501Srpaulocheck_established: 181214501Srpaulo if (data->established && data->ssl.state != WAIT_FRAG_ACK) { 182214501Srpaulo /* TLS handshake has been completed and there are no more 183214501Srpaulo * fragments waiting to be sent out. */ 184214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); 185214501Srpaulo eap_tls_state(data, SUCCESS); 186214501Srpaulo } 187214501Srpaulo 188214501Srpaulo return res; 189214501Srpaulo} 190214501Srpaulo 191214501Srpaulo 192214501Srpaulostatic Boolean eap_tls_check(struct eap_sm *sm, void *priv, 193214501Srpaulo struct wpabuf *respData) 194214501Srpaulo{ 195252726Srpaulo struct eap_tls_data *data = priv; 196214501Srpaulo const u8 *pos; 197214501Srpaulo size_t len; 198214501Srpaulo 199252726Srpaulo if (data->eap_type == EAP_UNAUTH_TLS_TYPE) 200252726Srpaulo pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, 201252726Srpaulo EAP_VENDOR_TYPE_UNAUTH_TLS, respData, 202252726Srpaulo &len); 203281806Srpaulo else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) 204281806Srpaulo pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, 205281806Srpaulo EAP_VENDOR_WFA_UNAUTH_TLS, respData, 206281806Srpaulo &len); 207252726Srpaulo else 208252726Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, 209252726Srpaulo respData, &len); 210214501Srpaulo if (pos == NULL || len < 1) { 211214501Srpaulo wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); 212214501Srpaulo return TRUE; 213214501Srpaulo } 214214501Srpaulo 215214501Srpaulo return FALSE; 216214501Srpaulo} 217214501Srpaulo 218214501Srpaulo 219214501Srpaulostatic void eap_tls_process_msg(struct eap_sm *sm, void *priv, 220214501Srpaulo const struct wpabuf *respData) 221214501Srpaulo{ 222214501Srpaulo struct eap_tls_data *data = priv; 223214501Srpaulo if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) { 224214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " 225214501Srpaulo "handshake message"); 226214501Srpaulo return; 227214501Srpaulo } 228214501Srpaulo if (eap_server_tls_phase1(sm, &data->ssl) < 0) 229214501Srpaulo eap_tls_state(data, FAILURE); 230214501Srpaulo} 231214501Srpaulo 232214501Srpaulo 233214501Srpaulostatic void eap_tls_process(struct eap_sm *sm, void *priv, 234214501Srpaulo struct wpabuf *respData) 235214501Srpaulo{ 236214501Srpaulo struct eap_tls_data *data = priv; 237214501Srpaulo if (eap_server_tls_process(sm, &data->ssl, respData, data, 238252726Srpaulo data->eap_type, NULL, eap_tls_process_msg) < 239214501Srpaulo 0) 240214501Srpaulo eap_tls_state(data, FAILURE); 241214501Srpaulo} 242214501Srpaulo 243214501Srpaulo 244214501Srpaulostatic Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) 245214501Srpaulo{ 246214501Srpaulo struct eap_tls_data *data = priv; 247214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 248214501Srpaulo} 249214501Srpaulo 250214501Srpaulo 251214501Srpaulostatic u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) 252214501Srpaulo{ 253214501Srpaulo struct eap_tls_data *data = priv; 254214501Srpaulo u8 *eapKeyData; 255214501Srpaulo 256214501Srpaulo if (data->state != SUCCESS) 257214501Srpaulo return NULL; 258214501Srpaulo 259214501Srpaulo eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, 260214501Srpaulo "client EAP encryption", 261214501Srpaulo EAP_TLS_KEY_LEN); 262214501Srpaulo if (eapKeyData) { 263214501Srpaulo *len = EAP_TLS_KEY_LEN; 264214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", 265214501Srpaulo eapKeyData, EAP_TLS_KEY_LEN); 266214501Srpaulo } else { 267214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); 268214501Srpaulo } 269214501Srpaulo 270214501Srpaulo return eapKeyData; 271214501Srpaulo} 272214501Srpaulo 273214501Srpaulo 274214501Srpaulostatic u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 275214501Srpaulo{ 276214501Srpaulo struct eap_tls_data *data = priv; 277214501Srpaulo u8 *eapKeyData, *emsk; 278214501Srpaulo 279214501Srpaulo if (data->state != SUCCESS) 280214501Srpaulo return NULL; 281214501Srpaulo 282214501Srpaulo eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, 283214501Srpaulo "client EAP encryption", 284214501Srpaulo EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 285214501Srpaulo if (eapKeyData) { 286214501Srpaulo emsk = os_malloc(EAP_EMSK_LEN); 287214501Srpaulo if (emsk) 288214501Srpaulo os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, 289214501Srpaulo EAP_EMSK_LEN); 290281806Srpaulo bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 291214501Srpaulo } else 292214501Srpaulo emsk = NULL; 293214501Srpaulo 294214501Srpaulo if (emsk) { 295214501Srpaulo *len = EAP_EMSK_LEN; 296214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK", 297214501Srpaulo emsk, EAP_EMSK_LEN); 298214501Srpaulo } else { 299214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK"); 300214501Srpaulo } 301214501Srpaulo 302214501Srpaulo return emsk; 303214501Srpaulo} 304214501Srpaulo 305214501Srpaulo 306214501Srpaulostatic Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) 307214501Srpaulo{ 308214501Srpaulo struct eap_tls_data *data = priv; 309214501Srpaulo return data->state == SUCCESS; 310214501Srpaulo} 311214501Srpaulo 312214501Srpaulo 313281806Srpaulostatic u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 314281806Srpaulo{ 315281806Srpaulo struct eap_tls_data *data = priv; 316281806Srpaulo 317281806Srpaulo if (data->state != SUCCESS) 318281806Srpaulo return NULL; 319281806Srpaulo 320281806Srpaulo return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS, 321281806Srpaulo len); 322281806Srpaulo} 323281806Srpaulo 324281806Srpaulo 325214501Srpauloint eap_server_tls_register(void) 326214501Srpaulo{ 327214501Srpaulo struct eap_method *eap; 328214501Srpaulo int ret; 329214501Srpaulo 330214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 331214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); 332214501Srpaulo if (eap == NULL) 333214501Srpaulo return -1; 334214501Srpaulo 335214501Srpaulo eap->init = eap_tls_init; 336214501Srpaulo eap->reset = eap_tls_reset; 337214501Srpaulo eap->buildReq = eap_tls_buildReq; 338214501Srpaulo eap->check = eap_tls_check; 339214501Srpaulo eap->process = eap_tls_process; 340214501Srpaulo eap->isDone = eap_tls_isDone; 341214501Srpaulo eap->getKey = eap_tls_getKey; 342214501Srpaulo eap->isSuccess = eap_tls_isSuccess; 343214501Srpaulo eap->get_emsk = eap_tls_get_emsk; 344281806Srpaulo eap->getSessionId = eap_tls_get_session_id; 345214501Srpaulo 346214501Srpaulo ret = eap_server_method_register(eap); 347214501Srpaulo if (ret) 348214501Srpaulo eap_server_method_free(eap); 349214501Srpaulo return ret; 350214501Srpaulo} 351252726Srpaulo 352252726Srpaulo 353252726Srpaulo#ifdef EAP_SERVER_UNAUTH_TLS 354252726Srpauloint eap_server_unauth_tls_register(void) 355252726Srpaulo{ 356252726Srpaulo struct eap_method *eap; 357252726Srpaulo int ret; 358252726Srpaulo 359252726Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 360252726Srpaulo EAP_VENDOR_UNAUTH_TLS, 361252726Srpaulo EAP_VENDOR_TYPE_UNAUTH_TLS, 362252726Srpaulo "UNAUTH-TLS"); 363252726Srpaulo if (eap == NULL) 364252726Srpaulo return -1; 365252726Srpaulo 366252726Srpaulo eap->init = eap_unauth_tls_init; 367252726Srpaulo eap->reset = eap_tls_reset; 368252726Srpaulo eap->buildReq = eap_tls_buildReq; 369252726Srpaulo eap->check = eap_tls_check; 370252726Srpaulo eap->process = eap_tls_process; 371252726Srpaulo eap->isDone = eap_tls_isDone; 372252726Srpaulo eap->getKey = eap_tls_getKey; 373252726Srpaulo eap->isSuccess = eap_tls_isSuccess; 374252726Srpaulo eap->get_emsk = eap_tls_get_emsk; 375252726Srpaulo 376252726Srpaulo ret = eap_server_method_register(eap); 377252726Srpaulo if (ret) 378252726Srpaulo eap_server_method_free(eap); 379252726Srpaulo return ret; 380252726Srpaulo} 381252726Srpaulo#endif /* EAP_SERVER_UNAUTH_TLS */ 382281806Srpaulo 383281806Srpaulo 384281806Srpaulo#ifdef CONFIG_HS20 385281806Srpauloint eap_server_wfa_unauth_tls_register(void) 386281806Srpaulo{ 387281806Srpaulo struct eap_method *eap; 388281806Srpaulo int ret; 389281806Srpaulo 390281806Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 391281806Srpaulo EAP_VENDOR_WFA_NEW, 392281806Srpaulo EAP_VENDOR_WFA_UNAUTH_TLS, 393281806Srpaulo "WFA-UNAUTH-TLS"); 394281806Srpaulo if (eap == NULL) 395281806Srpaulo return -1; 396281806Srpaulo 397281806Srpaulo eap->init = eap_wfa_unauth_tls_init; 398281806Srpaulo eap->reset = eap_tls_reset; 399281806Srpaulo eap->buildReq = eap_tls_buildReq; 400281806Srpaulo eap->check = eap_tls_check; 401281806Srpaulo eap->process = eap_tls_process; 402281806Srpaulo eap->isDone = eap_tls_isDone; 403281806Srpaulo eap->getKey = eap_tls_getKey; 404281806Srpaulo eap->isSuccess = eap_tls_isSuccess; 405281806Srpaulo eap->get_emsk = eap_tls_get_emsk; 406281806Srpaulo 407281806Srpaulo ret = eap_server_method_register(eap); 408281806Srpaulo if (ret) 409281806Srpaulo eap_server_method_free(eap); 410281806Srpaulo return ret; 411281806Srpaulo} 412281806Srpaulo#endif /* CONFIG_HS20 */ 413