1189251Ssam/* 2189251Ssam * EAP peer method: EAP-TLS (RFC 2716) 3252726Srpaulo * Copyright (c) 2004-2008, 2012, 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/tls.h" 13189251Ssam#include "eap_i.h" 14189251Ssam#include "eap_tls_common.h" 15189251Ssam#include "eap_config.h" 16189251Ssam 17189251Ssam 18189251Ssamstatic void eap_tls_deinit(struct eap_sm *sm, void *priv); 19189251Ssam 20189251Ssam 21189251Ssamstruct eap_tls_data { 22189251Ssam struct eap_ssl_data ssl; 23189251Ssam u8 *key_data; 24252726Srpaulo void *ssl_ctx; 25252726Srpaulo u8 eap_type; 26189251Ssam}; 27189251Ssam 28189251Ssam 29189251Ssamstatic void * eap_tls_init(struct eap_sm *sm) 30189251Ssam{ 31189251Ssam struct eap_tls_data *data; 32189251Ssam struct eap_peer_config *config = eap_get_config(sm); 33189251Ssam if (config == NULL || 34189251Ssam ((sm->init_phase2 ? config->private_key2 : config->private_key) 35189251Ssam == NULL && 36189251Ssam (sm->init_phase2 ? config->engine2 : config->engine) == 0)) { 37189251Ssam wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); 38189251Ssam return NULL; 39189251Ssam } 40189251Ssam 41189251Ssam data = os_zalloc(sizeof(*data)); 42189251Ssam if (data == NULL) 43189251Ssam return NULL; 44189251Ssam 45252726Srpaulo data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : 46252726Srpaulo sm->ssl_ctx; 47252726Srpaulo 48252726Srpaulo if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { 49189251Ssam wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); 50189251Ssam eap_tls_deinit(sm, data); 51189251Ssam if (config->engine) { 52189251Ssam wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard " 53189251Ssam "PIN"); 54189251Ssam eap_sm_request_pin(sm); 55189251Ssam sm->ignore = TRUE; 56189251Ssam } else if (config->private_key && !config->private_key_passwd) 57189251Ssam { 58189251Ssam wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " 59189251Ssam "key passphrase"); 60189251Ssam eap_sm_request_passphrase(sm); 61189251Ssam sm->ignore = TRUE; 62189251Ssam } 63189251Ssam return NULL; 64189251Ssam } 65189251Ssam 66252726Srpaulo data->eap_type = EAP_TYPE_TLS; 67252726Srpaulo 68189251Ssam return data; 69189251Ssam} 70189251Ssam 71189251Ssam 72252726Srpaulo#ifdef EAP_UNAUTH_TLS 73252726Srpaulostatic void * eap_unauth_tls_init(struct eap_sm *sm) 74252726Srpaulo{ 75252726Srpaulo struct eap_tls_data *data; 76252726Srpaulo struct eap_peer_config *config = eap_get_config(sm); 77252726Srpaulo 78252726Srpaulo data = os_zalloc(sizeof(*data)); 79252726Srpaulo if (data == NULL) 80252726Srpaulo return NULL; 81252726Srpaulo 82252726Srpaulo data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : 83252726Srpaulo sm->ssl_ctx; 84252726Srpaulo 85252726Srpaulo if (eap_peer_tls_ssl_init(sm, &data->ssl, config, 86252726Srpaulo EAP_UNAUTH_TLS_TYPE)) { 87252726Srpaulo wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); 88252726Srpaulo eap_tls_deinit(sm, data); 89252726Srpaulo return NULL; 90252726Srpaulo } 91252726Srpaulo 92252726Srpaulo data->eap_type = EAP_UNAUTH_TLS_TYPE; 93252726Srpaulo 94252726Srpaulo return data; 95252726Srpaulo} 96252726Srpaulo#endif /* EAP_UNAUTH_TLS */ 97252726Srpaulo 98252726Srpaulo 99189251Ssamstatic void eap_tls_deinit(struct eap_sm *sm, void *priv) 100189251Ssam{ 101189251Ssam struct eap_tls_data *data = priv; 102189251Ssam if (data == NULL) 103189251Ssam return; 104189251Ssam eap_peer_tls_ssl_deinit(sm, &data->ssl); 105189251Ssam os_free(data->key_data); 106189251Ssam os_free(data); 107189251Ssam} 108189251Ssam 109189251Ssam 110189251Ssamstatic struct wpabuf * eap_tls_failure(struct eap_sm *sm, 111189251Ssam struct eap_tls_data *data, 112189251Ssam struct eap_method_ret *ret, int res, 113189251Ssam struct wpabuf *resp, u8 id) 114189251Ssam{ 115189251Ssam wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); 116189251Ssam 117189251Ssam ret->methodState = METHOD_DONE; 118189251Ssam ret->decision = DECISION_FAIL; 119189251Ssam 120189251Ssam if (res == -1) { 121189251Ssam struct eap_peer_config *config = eap_get_config(sm); 122189251Ssam if (config) { 123189251Ssam /* 124189251Ssam * The TLS handshake failed. So better forget the old 125189251Ssam * PIN. It may be wrong, we cannot be sure but trying 126189251Ssam * the wrong one again might block it on the card--so 127189251Ssam * better ask the user again. 128189251Ssam */ 129189251Ssam os_free(config->pin); 130189251Ssam config->pin = NULL; 131189251Ssam } 132189251Ssam } 133189251Ssam 134189251Ssam if (resp) { 135189251Ssam /* 136189251Ssam * This is likely an alert message, so send it instead of just 137189251Ssam * ACKing the error. 138189251Ssam */ 139189251Ssam return resp; 140189251Ssam } 141189251Ssam 142252726Srpaulo return eap_peer_tls_build_ack(id, data->eap_type, 0); 143189251Ssam} 144189251Ssam 145189251Ssam 146189251Ssamstatic void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, 147189251Ssam struct eap_method_ret *ret) 148189251Ssam{ 149189251Ssam wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); 150189251Ssam 151189251Ssam ret->methodState = METHOD_DONE; 152189251Ssam ret->decision = DECISION_UNCOND_SUCC; 153189251Ssam 154189251Ssam os_free(data->key_data); 155189251Ssam data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, 156189251Ssam "client EAP encryption", 157189251Ssam EAP_TLS_KEY_LEN + 158189251Ssam EAP_EMSK_LEN); 159189251Ssam if (data->key_data) { 160189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", 161189251Ssam data->key_data, EAP_TLS_KEY_LEN); 162189251Ssam wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK", 163189251Ssam data->key_data + EAP_TLS_KEY_LEN, 164189251Ssam EAP_EMSK_LEN); 165189251Ssam } else { 166189251Ssam wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); 167189251Ssam } 168189251Ssam} 169189251Ssam 170189251Ssam 171189251Ssamstatic struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, 172189251Ssam struct eap_method_ret *ret, 173189251Ssam const struct wpabuf *reqData) 174189251Ssam{ 175189251Ssam size_t left; 176189251Ssam int res; 177189251Ssam struct wpabuf *resp; 178189251Ssam u8 flags, id; 179189251Ssam const u8 *pos; 180189251Ssam struct eap_tls_data *data = priv; 181189251Ssam 182252726Srpaulo pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, 183189251Ssam reqData, &left, &flags); 184189251Ssam if (pos == NULL) 185189251Ssam return NULL; 186189251Ssam id = eap_get_id(reqData); 187189251Ssam 188189251Ssam if (flags & EAP_TLS_FLAGS_START) { 189189251Ssam wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); 190189251Ssam left = 0; /* make sure that this frame is empty, even though it 191189251Ssam * should always be, anyway */ 192189251Ssam } 193189251Ssam 194189251Ssam resp = NULL; 195252726Srpaulo res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, 196252726Srpaulo id, pos, left, &resp); 197189251Ssam 198189251Ssam if (res < 0) { 199189251Ssam return eap_tls_failure(sm, data, ret, res, resp, id); 200189251Ssam } 201189251Ssam 202252726Srpaulo if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) 203189251Ssam eap_tls_success(sm, data, ret); 204189251Ssam 205189251Ssam if (res == 1) { 206189251Ssam wpabuf_free(resp); 207252726Srpaulo return eap_peer_tls_build_ack(id, data->eap_type, 0); 208189251Ssam } 209189251Ssam 210189251Ssam return resp; 211189251Ssam} 212189251Ssam 213189251Ssam 214189251Ssamstatic Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) 215189251Ssam{ 216189251Ssam struct eap_tls_data *data = priv; 217252726Srpaulo return tls_connection_established(data->ssl_ctx, data->ssl.conn); 218189251Ssam} 219189251Ssam 220189251Ssam 221189251Ssamstatic void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) 222189251Ssam{ 223189251Ssam} 224189251Ssam 225189251Ssam 226189251Ssamstatic void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) 227189251Ssam{ 228189251Ssam struct eap_tls_data *data = priv; 229189251Ssam os_free(data->key_data); 230189251Ssam data->key_data = NULL; 231189251Ssam if (eap_peer_tls_reauth_init(sm, &data->ssl)) { 232189251Ssam os_free(data); 233189251Ssam return NULL; 234189251Ssam } 235189251Ssam return priv; 236189251Ssam} 237189251Ssam 238189251Ssam 239189251Ssamstatic int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, 240189251Ssam size_t buflen, int verbose) 241189251Ssam{ 242189251Ssam struct eap_tls_data *data = priv; 243189251Ssam return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); 244189251Ssam} 245189251Ssam 246189251Ssam 247189251Ssamstatic Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) 248189251Ssam{ 249189251Ssam struct eap_tls_data *data = priv; 250189251Ssam return data->key_data != NULL; 251189251Ssam} 252189251Ssam 253189251Ssam 254189251Ssamstatic u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) 255189251Ssam{ 256189251Ssam struct eap_tls_data *data = priv; 257189251Ssam u8 *key; 258189251Ssam 259189251Ssam if (data->key_data == NULL) 260189251Ssam return NULL; 261189251Ssam 262189251Ssam key = os_malloc(EAP_TLS_KEY_LEN); 263189251Ssam if (key == NULL) 264189251Ssam return NULL; 265189251Ssam 266189251Ssam *len = EAP_TLS_KEY_LEN; 267189251Ssam os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); 268189251Ssam 269189251Ssam return key; 270189251Ssam} 271189251Ssam 272189251Ssam 273189251Ssamstatic u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 274189251Ssam{ 275189251Ssam struct eap_tls_data *data = priv; 276189251Ssam u8 *key; 277189251Ssam 278189251Ssam if (data->key_data == NULL) 279189251Ssam return NULL; 280189251Ssam 281189251Ssam key = os_malloc(EAP_EMSK_LEN); 282189251Ssam if (key == NULL) 283189251Ssam return NULL; 284189251Ssam 285189251Ssam *len = EAP_EMSK_LEN; 286189251Ssam os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); 287189251Ssam 288189251Ssam return key; 289189251Ssam} 290189251Ssam 291189251Ssam 292189251Ssamint eap_peer_tls_register(void) 293189251Ssam{ 294189251Ssam struct eap_method *eap; 295189251Ssam int ret; 296189251Ssam 297189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 298189251Ssam EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); 299189251Ssam if (eap == NULL) 300189251Ssam return -1; 301189251Ssam 302189251Ssam eap->init = eap_tls_init; 303189251Ssam eap->deinit = eap_tls_deinit; 304189251Ssam eap->process = eap_tls_process; 305189251Ssam eap->isKeyAvailable = eap_tls_isKeyAvailable; 306189251Ssam eap->getKey = eap_tls_getKey; 307189251Ssam eap->get_status = eap_tls_get_status; 308189251Ssam eap->has_reauth_data = eap_tls_has_reauth_data; 309189251Ssam eap->deinit_for_reauth = eap_tls_deinit_for_reauth; 310189251Ssam eap->init_for_reauth = eap_tls_init_for_reauth; 311189251Ssam eap->get_emsk = eap_tls_get_emsk; 312189251Ssam 313189251Ssam ret = eap_peer_method_register(eap); 314189251Ssam if (ret) 315189251Ssam eap_peer_method_free(eap); 316189251Ssam return ret; 317189251Ssam} 318252726Srpaulo 319252726Srpaulo 320252726Srpaulo#ifdef EAP_UNAUTH_TLS 321252726Srpauloint eap_peer_unauth_tls_register(void) 322252726Srpaulo{ 323252726Srpaulo struct eap_method *eap; 324252726Srpaulo int ret; 325252726Srpaulo 326252726Srpaulo eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 327252726Srpaulo EAP_VENDOR_UNAUTH_TLS, 328252726Srpaulo EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); 329252726Srpaulo if (eap == NULL) 330252726Srpaulo return -1; 331252726Srpaulo 332252726Srpaulo eap->init = eap_unauth_tls_init; 333252726Srpaulo eap->deinit = eap_tls_deinit; 334252726Srpaulo eap->process = eap_tls_process; 335252726Srpaulo eap->isKeyAvailable = eap_tls_isKeyAvailable; 336252726Srpaulo eap->getKey = eap_tls_getKey; 337252726Srpaulo eap->get_status = eap_tls_get_status; 338252726Srpaulo eap->has_reauth_data = eap_tls_has_reauth_data; 339252726Srpaulo eap->deinit_for_reauth = eap_tls_deinit_for_reauth; 340252726Srpaulo eap->init_for_reauth = eap_tls_init_for_reauth; 341252726Srpaulo eap->get_emsk = eap_tls_get_emsk; 342252726Srpaulo 343252726Srpaulo ret = eap_peer_method_register(eap); 344252726Srpaulo if (ret) 345252726Srpaulo eap_peer_method_free(eap); 346252726Srpaulo return ret; 347252726Srpaulo} 348252726Srpaulo#endif /* EAP_UNAUTH_TLS */ 349