diff options
Diffstat (limited to 'eap_peap.c')
-rw-r--r-- | eap_peap.c | 895 |
1 files changed, 0 insertions, 895 deletions
diff --git a/eap_peap.c b/eap_peap.c deleted file mode 100644 index 12bd8d8e2f9e..000000000000 --- a/eap_peap.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "config_ssid.h" -#include "tls.h" -#include "eap_tlv.h" - - -/* Maximum supported PEAP version - * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt - * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt - * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt - */ -#define EAP_PEAP_VERSION 1 - - -static void eap_peap_deinit(struct eap_sm *sm, void *priv); - - -struct eap_peap_data { - struct eap_ssl_data ssl; - - int peap_version, force_peap_version, force_new_label; - - const struct eap_method *phase2_method; - void *phase2_priv; - int phase2_success; - int phase2_eap_success; - int phase2_eap_started; - - struct eap_method_type phase2_type; - struct eap_method_type *phase2_types; - size_t num_phase2_types; - - int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner - * EAP-Success - * 1 = reply with tunneled EAP-Success to inner - * EAP-Success and expect AS to send outer - * (unencrypted) EAP-Success after this - * 2 = reply with PEAP/TLS ACK to inner - * EAP-Success and expect AS to send outer - * (unencrypted) EAP-Success after this */ - int resuming; /* starting a resumed session */ - u8 *key_data; - - u8 *pending_phase2_req; - size_t pending_phase2_req_len; -}; - - -static void * eap_peap_init(struct eap_sm *sm) -{ - struct eap_peap_data *data; - struct wpa_ssid *config = eap_get_config(sm); - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - sm->peap_done = FALSE; - data->peap_version = EAP_PEAP_VERSION; - data->force_peap_version = -1; - data->peap_outer_success = 2; - - if (config && config->phase1) { - char *pos = os_strstr(config->phase1, "peapver="); - if (pos) { - data->force_peap_version = atoi(pos + 8); - data->peap_version = data->force_peap_version; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version " - "%d", data->force_peap_version); - } - - if (os_strstr(config->phase1, "peaplabel=1")) { - data->force_new_label = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for " - "key derivation"); - } - - if (os_strstr(config->phase1, "peap_outer_success=0")) { - data->peap_outer_success = 0; - wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate " - "authentication on tunneled EAP-Success"); - } else if (os_strstr(config->phase1, "peap_outer_success=1")) { - data->peap_outer_success = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled " - "EAP-Success after receiving tunneled " - "EAP-Success"); - } else if (os_strstr(config->phase1, "peap_outer_success=2")) { - data->peap_outer_success = 2; - wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK " - "after receiving tunneled EAP-Success"); - } - } - - if (config && config->phase2) { - char *start, *pos, *buf; - struct eap_method_type *methods = NULL, *_methods; - u8 method; - size_t num_methods = 0; - start = buf = os_strdup(config->phase2); - if (buf == NULL) { - eap_peap_deinit(sm, data); - return NULL; - } - while (start && *start != '\0') { - int vendor; - pos = os_strstr(start, "auth="); - if (pos == NULL) - break; - if (start != pos && *(pos - 1) != ' ') { - start = pos + 5; - continue; - } - - start = pos + 5; - pos = os_strchr(start, ' '); - if (pos) - *pos++ = '\0'; - method = eap_get_phase2_type(start, &vendor); - if (vendor == EAP_VENDOR_IETF && - method == EAP_TYPE_NONE) { - wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported " - "Phase2 method '%s'", start); - } else { - num_methods++; - _methods = os_realloc( - methods, - num_methods * sizeof(*methods)); - if (_methods == NULL) { - os_free(methods); - os_free(buf); - eap_peap_deinit(sm, data); - return NULL; - } - methods = _methods; - methods[num_methods - 1].vendor = vendor; - methods[num_methods - 1].method = method; - } - - start = pos; - } - os_free(buf); - data->phase2_types = methods; - data->num_phase2_types = num_methods; - } - if (data->phase2_types == NULL) { - data->phase2_types = - eap_get_phase2_types(config, &data->num_phase2_types); - } - if (data->phase2_types == NULL) { - wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available"); - eap_peap_deinit(sm, data); - return NULL; - } - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types", - (u8 *) data->phase2_types, - data->num_phase2_types * sizeof(struct eap_method_type)); - data->phase2_type.vendor = EAP_VENDOR_IETF; - data->phase2_type.method = EAP_TYPE_NONE; - - if (eap_tls_ssl_init(sm, &data->ssl, config)) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); - eap_peap_deinit(sm, data); - return NULL; - } - - return data; -} - - -static void eap_peap_deinit(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - if (data == NULL) - return; - if (data->phase2_priv && data->phase2_method) - data->phase2_method->deinit(sm, data->phase2_priv); - os_free(data->phase2_types); - eap_tls_ssl_deinit(sm, &data->ssl); - os_free(data->key_data); - os_free(data->pending_phase2_req); - os_free(data); -} - - -static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, - int id, const u8 *plain, size_t plain_len, - u8 **out_data, size_t *out_len) -{ - int res; - u8 *pos; - struct eap_hdr *resp; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. - * Note: Microsoft IAS did not seem to like TLS Message Length with - * PEAP/MSCHAPv2. */ - resp = os_malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); - if (resp == NULL) - return -1; - - resp->code = EAP_CODE_RESPONSE; - resp->identifier = id; - - pos = (u8 *) (resp + 1); - *pos++ = EAP_TYPE_PEAP; - *pos++ = data->peap_version; - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, - pos, data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " - "data"); - os_free(resp); - return -1; - } - - *out_len = sizeof(struct eap_hdr) + 2 + res; - resp->length = host_to_be16(*out_len); - *out_data = (u8 *) resp; - return 0; -} - - -static int eap_peap_phase2_nak(struct eap_peap_data *data, struct eap_hdr *hdr, - u8 **resp, size_t *resp_len) -{ - struct eap_hdr *resp_hdr; - u8 *pos = (u8 *) (hdr + 1); - size_t i; - - /* TODO: add support for expanded Nak */ - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos); - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types", - (u8 *) data->phase2_types, - data->num_phase2_types * sizeof(struct eap_method_type)); - *resp_len = sizeof(struct eap_hdr) + 1; - *resp = os_malloc(*resp_len + data->num_phase2_types); - if (*resp == NULL) - return -1; - - resp_hdr = (struct eap_hdr *) (*resp); - resp_hdr->code = EAP_CODE_RESPONSE; - resp_hdr->identifier = hdr->identifier; - pos = (u8 *) (resp_hdr + 1); - *pos++ = EAP_TYPE_NAK; - for (i = 0; i < data->num_phase2_types; i++) { - if (data->phase2_types[i].vendor == EAP_VENDOR_IETF && - data->phase2_types[i].method < 256) { - (*resp_len)++; - *pos++ = data->phase2_types[i].method; - } - } - resp_hdr->length = host_to_be16(*resp_len); - - return 0; -} - - -static int eap_peap_phase2_request(struct eap_sm *sm, - struct eap_peap_data *data, - struct eap_method_ret *ret, - struct eap_hdr *hdr, - u8 **resp, size_t *resp_len) -{ - size_t len = be_to_host16(hdr->length); - u8 *pos; - struct eap_method_ret iret; - struct wpa_ssid *config = eap_get_config(sm); - - if (len <= sizeof(struct eap_hdr)) { - wpa_printf(MSG_INFO, "EAP-PEAP: too short " - "Phase 2 request (len=%lu)", (unsigned long) len); - return -1; - } - pos = (u8 *) (hdr + 1); - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); - switch (*pos) { - case EAP_TYPE_IDENTITY: - *resp = eap_sm_buildIdentity(sm, hdr->identifier, resp_len, 1); - break; - case EAP_TYPE_TLV: - os_memset(&iret, 0, sizeof(iret)); - if (eap_tlv_process(sm, &iret, hdr, resp, resp_len, - data->phase2_eap_started && - !data->phase2_eap_success)) { - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - return -1; - } - if (iret.methodState == METHOD_DONE || - iret.methodState == METHOD_MAY_CONT) { - ret->methodState = iret.methodState; - ret->decision = iret.decision; - data->phase2_success = 1; - } - break; - default: - if (data->phase2_type.vendor == EAP_VENDOR_IETF && - data->phase2_type.method == EAP_TYPE_NONE) { - size_t i; - for (i = 0; i < data->num_phase2_types; i++) { - if (data->phase2_types[i].vendor != - EAP_VENDOR_IETF || - data->phase2_types[i].method != *pos) - continue; - - data->phase2_type.vendor = - data->phase2_types[i].vendor; - data->phase2_type.method = - data->phase2_types[i].method; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " - "Phase 2 EAP vendor %d method %d", - data->phase2_type.vendor, - data->phase2_type.method); - break; - } - } - if (*pos != data->phase2_type.method || - *pos == EAP_TYPE_NONE) { - if (eap_peap_phase2_nak(data, hdr, resp, resp_len)) - return -1; - return 0; - } - - if (data->phase2_priv == NULL) { - data->phase2_method = eap_sm_get_eap_methods( - data->phase2_type.vendor, - data->phase2_type.method); - if (data->phase2_method) { - sm->init_phase2 = 1; - data->phase2_priv = - data->phase2_method->init(sm); - sm->init_phase2 = 0; - } - } - if (data->phase2_priv == NULL || data->phase2_method == NULL) { - wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " - "Phase 2 EAP method %d", *pos); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - return -1; - } - data->phase2_eap_started = 1; - os_memset(&iret, 0, sizeof(iret)); - *resp = data->phase2_method->process(sm, data->phase2_priv, - &iret, (u8 *) hdr, len, - resp_len); - if ((iret.methodState == METHOD_DONE || - iret.methodState == METHOD_MAY_CONT) && - (iret.decision == DECISION_UNCOND_SUCC || - iret.decision == DECISION_COND_SUCC)) { - data->phase2_eap_success = 1; - data->phase2_success = 1; - } - break; - } - - if (*resp == NULL && - (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp || config->pending_req_new_password)) { - os_free(data->pending_phase2_req); - data->pending_phase2_req = os_malloc(len); - if (data->pending_phase2_req) { - os_memcpy(data->pending_phase2_req, hdr, len); - data->pending_phase2_req_len = len; - } - } - - return 0; -} - - -static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, - struct eap_method_ret *ret, - const struct eap_hdr *req, - const u8 *in_data, size_t in_len, - u8 **out_data, size_t *out_len) -{ - u8 *in_decrypted; - int res, skip_change = 0; - struct eap_hdr *hdr, *rhdr; - u8 *resp = NULL; - size_t resp_len, len_decrypted, len, buf_len; - const u8 *msg; - size_t msg_len; - int need_more_input; - - wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); - - if (data->pending_phase2_req) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " - "skip decryption and use old data"); - /* Clear TLS reassembly state. */ - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - data->ssl.tls_in_left = 0; - data->ssl.tls_in_total = 0; - in_decrypted = data->pending_phase2_req; - data->pending_phase2_req = NULL; - len_decrypted = data->pending_phase2_req_len; - skip_change = 1; - goto continue_req; - } - - msg = eap_tls_data_reassemble(sm, &data->ssl, in_data, in_len, - &msg_len, &need_more_input); - if (msg == NULL) - return need_more_input ? 1 : -1; - - if (in_len == 0 && sm->workaround && data->phase2_success) { - /* - * Cisco ACS seems to be using TLS ACK to terminate - * EAP-PEAPv0/GTC. Try to reply with TLS ACK. - */ - wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " - "expected data - acknowledge with TLS ACK since " - "Phase 2 has been completed"); - ret->decision = DECISION_COND_SUCC; - ret->methodState = METHOD_DONE; - return 1; - } - - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.tls_in_total; - in_decrypted = os_malloc(buf_len); - if (in_decrypted == NULL) { - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " - "for decryption"); - return -1; - } - - res = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - msg, msg_len, in_decrypted, buf_len); - os_free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " - "data"); - os_free(in_decrypted); - return 0; - } - len_decrypted = res; - -continue_req: - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted, - len_decrypted); - - hdr = (struct eap_hdr *) in_decrypted; - if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST && - be_to_host16(hdr->length) == 5 && - in_decrypted[4] == EAP_TYPE_IDENTITY) { - /* At least FreeRADIUS seems to send full EAP header with - * EAP Request Identity */ - skip_change = 1; - } - if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST && - in_decrypted[4] == EAP_TYPE_TLV) { - skip_change = 1; - } - - if (data->peap_version == 0 && !skip_change) { - struct eap_hdr *nhdr = os_malloc(sizeof(struct eap_hdr) + - len_decrypted); - if (nhdr == NULL) { - os_free(in_decrypted); - return 0; - } - os_memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); - os_free(in_decrypted); - nhdr->code = req->code; - nhdr->identifier = req->identifier; - nhdr->length = host_to_be16(sizeof(struct eap_hdr) + - len_decrypted); - - len_decrypted += sizeof(struct eap_hdr); - in_decrypted = (u8 *) nhdr; - } - hdr = (struct eap_hdr *) in_decrypted; - if (len_decrypted < sizeof(*hdr)) { - os_free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " - "EAP frame (len=%lu)", - (unsigned long) len_decrypted); - return 0; - } - len = be_to_host16(hdr->length); - if (len > len_decrypted) { - os_free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " - "Phase 2 EAP frame (len=%lu hdr->length=%lu)", - (unsigned long) len_decrypted, (unsigned long) len); - return 0; - } - if (len < len_decrypted) { - wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " - "shorter length than full decrypted data " - "(%lu < %lu)", - (unsigned long) len, (unsigned long) len_decrypted); - if (sm->workaround && len == 4 && len_decrypted == 5 && - in_decrypted[4] == EAP_TYPE_IDENTITY) { - /* Radiator 3.9 seems to set Phase 2 EAP header to use - * incorrect length for the EAP-Request Identity - * packet, so fix the inner header to interoperate.. - * This was fixed in 2004-06-23 patch for Radiator and - * this workaround can be removed at some point. */ - wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace " - "Phase 2 EAP header len (%lu) with real " - "decrypted len (%lu)", - (unsigned long) len, - (unsigned long) len_decrypted); - len = len_decrypted; - hdr->length = host_to_be16(len); - } - } - wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " - "identifier=%d length=%lu", hdr->code, hdr->identifier, - (unsigned long) len); - switch (hdr->code) { - case EAP_CODE_REQUEST: - if (eap_peap_phase2_request(sm, data, ret, hdr, - &resp, &resp_len)) { - os_free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " - "processing failed"); - return 0; - } - break; - case EAP_CODE_SUCCESS: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); - if (data->peap_version == 1) { - /* EAP-Success within TLS tunnel is used to indicate - * shutdown of the TLS channel. The authentication has - * been completed. */ - if (data->phase2_eap_started && - !data->phase2_eap_success) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " - "Success used to indicate success, " - "but Phase 2 EAP was not yet " - "completed successfully"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - os_free(in_decrypted); - return 0; - } - wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " - "EAP-Success within TLS tunnel - " - "authentication completed"); - ret->decision = DECISION_UNCOND_SUCC; - ret->methodState = METHOD_DONE; - data->phase2_success = 1; - if (data->peap_outer_success == 2) { - os_free(in_decrypted); - wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " - "to finish authentication"); - return 1; - } else if (data->peap_outer_success == 1) { - /* Reply with EAP-Success within the TLS - * channel to complete the authentication. */ - resp_len = sizeof(struct eap_hdr); - resp = os_zalloc(resp_len); - if (resp) { - rhdr = (struct eap_hdr *) resp; - rhdr->code = EAP_CODE_SUCCESS; - rhdr->identifier = hdr->identifier; - rhdr->length = host_to_be16(resp_len); - } - } else { - /* No EAP-Success expected for Phase 1 (outer, - * unencrypted auth), so force EAP state - * machine to SUCCESS state. */ - sm->peap_done = TRUE; - } - } else { - /* FIX: ? */ - } - break; - case EAP_CODE_FAILURE: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); - ret->decision = DECISION_FAIL; - ret->methodState = METHOD_MAY_CONT; - ret->allowNotifications = FALSE; - /* Reply with EAP-Failure within the TLS channel to complete - * failure reporting. */ - resp_len = sizeof(struct eap_hdr); - resp = os_zalloc(resp_len); - if (resp) { - rhdr = (struct eap_hdr *) resp; - rhdr->code = EAP_CODE_FAILURE; - rhdr->identifier = hdr->identifier; - rhdr->length = host_to_be16(resp_len); - } - break; - default: - wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " - "Phase 2 EAP header", hdr->code); - break; - } - - os_free(in_decrypted); - - if (resp) { - u8 *resp_pos; - size_t resp_send_len; - int skip_change2 = 0; - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", - resp, resp_len); - /* PEAP version changes */ - if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE && - resp[4] == EAP_TYPE_TLV) - skip_change2 = 1; - if (data->peap_version == 0 && !skip_change2) { - resp_pos = resp + sizeof(struct eap_hdr); - resp_send_len = resp_len - sizeof(struct eap_hdr); - } else { - resp_pos = resp; - resp_send_len = resp_len; - } - - if (eap_peap_encrypt(sm, data, req->identifier, - resp_pos, resp_send_len, - out_data, out_len)) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " - "a Phase 2 frame"); - } - os_free(resp); - } - - return 0; -} - - -static u8 * eap_peap_process(struct eap_sm *sm, void *priv, - struct eap_method_ret *ret, - const u8 *reqData, size_t reqDataLen, - size_t *respDataLen) -{ - const struct eap_hdr *req; - size_t left; - int res; - u8 flags, *resp, id; - const u8 *pos; - struct eap_peap_data *data = priv; - - pos = eap_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, - reqData, reqDataLen, &left, &flags); - if (pos == NULL) - return NULL; - req = (const struct eap_hdr *) reqData; - id = req->identifier; - - if (flags & EAP_TLS_FLAGS_START) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " - "ver=%d)", flags & EAP_PEAP_VERSION_MASK, - data->peap_version); - if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) - data->peap_version = flags & EAP_PEAP_VERSION_MASK; - if (data->force_peap_version >= 0 && - data->force_peap_version != data->peap_version) { - wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " - "forced PEAP version %d", - data->force_peap_version); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - ret->allowNotifications = FALSE; - return NULL; - } - wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", - data->peap_version); - left = 0; /* make sure that this frame is empty, even though it - * should always be, anyway */ - } - - resp = NULL; - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && - !data->resuming) { - res = eap_peap_decrypt(sm, data, ret, req, pos, left, - &resp, respDataLen); - } else { - res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, pos, left, - &resp, respDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - char *label; - wpa_printf(MSG_DEBUG, - "EAP-PEAP: TLS done, proceed to Phase 2"); - os_free(data->key_data); - /* draft-josefsson-ppext-eap-tls-eap-05.txt - * specifies that PEAPv1 would use "client PEAP - * encryption" as the label. However, most existing - * PEAPv1 implementations seem to be using the old - * label, "client EAP encryption", instead. Use the old - * label by default, but allow it to be configured with - * phase1 parameter peaplabel=1. */ - if (data->peap_version > 1 || data->force_new_label) - label = "client PEAP encryption"; - else - label = "client EAP encryption"; - wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " - "key derivation", label); - data->key_data = - eap_tls_derive_key(sm, &data->ssl, label, - EAP_TLS_KEY_LEN); - if (data->key_data) { - wpa_hexdump_key(MSG_DEBUG, - "EAP-PEAP: Derived key", - data->key_data, - EAP_TLS_KEY_LEN); - } else { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " - "derive key"); - } - - if (sm->workaround && data->resuming) { - /* - * At least few RADIUS servers (Aegis v1.1.6; - * but not v1.1.4; and Cisco ACS) seem to be - * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco - * ACS) session resumption with outer - * EAP-Success. This does not seem to follow - * draft-josefsson-pppext-eap-tls-eap-05.txt - * section 4.2, so only allow this if EAP - * workarounds are enabled. - */ - wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " - "allow outer EAP-Success to " - "terminate PEAP resumption"); - ret->decision = DECISION_COND_SUCC; - data->phase2_success = 1; - } - - data->resuming = 0; - } - - if (res == 2) { - /* - * Application data included in the handshake message. - */ - os_free(data->pending_phase2_req); - data->pending_phase2_req = resp; - data->pending_phase2_req_len = *respDataLen; - resp = NULL; - *respDataLen = 0; - res = eap_peap_decrypt(sm, data, ret, req, pos, left, - &resp, respDataLen); - } - } - - if (ret->methodState == METHOD_DONE) { - ret->allowNotifications = FALSE; - } - - if (res == 1) { - return eap_tls_build_ack(&data->ssl, respDataLen, id, - EAP_TYPE_PEAP, data->peap_version); - } - - return resp; -} - - -static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && - data->phase2_success; -} - - -static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - os_free(data->pending_phase2_req); - data->pending_phase2_req = NULL; -} - - -static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - os_free(data->key_data); - data->key_data = NULL; - if (eap_tls_reauth_init(sm, &data->ssl)) { - os_free(data); - return NULL; - } - if (data->phase2_priv && data->phase2_method && - data->phase2_method->init_for_reauth) - data->phase2_method->init_for_reauth(sm, data->phase2_priv); - data->phase2_success = 0; - data->phase2_eap_success = 0; - data->phase2_eap_started = 0; - data->resuming = 1; - sm->peap_done = FALSE; - return priv; -} - - -static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, - size_t buflen, int verbose) -{ - struct eap_peap_data *data = priv; - int len, ret; - - len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose); - if (data->phase2_method) { - ret = os_snprintf(buf + len, buflen - len, - "EAP-PEAPv%d Phase2 method=%s\n", - data->peap_version, - data->phase2_method->name); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; - } - return len; -} - - -static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return data->key_data != NULL && data->phase2_success; -} - - -static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_peap_data *data = priv; - u8 *key; - - if (data->key_data == NULL || !data->phase2_success) - return NULL; - - key = os_malloc(EAP_TLS_KEY_LEN); - if (key == NULL) - return NULL; - - *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); - - return key; -} - - -int eap_peer_peap_register(void) -{ - struct eap_method *eap; - int ret; - - eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); - if (eap == NULL) - return -1; - - eap->init = eap_peap_init; - eap->deinit = eap_peap_deinit; - eap->process = eap_peap_process; - eap->isKeyAvailable = eap_peap_isKeyAvailable; - eap->getKey = eap_peap_getKey; - eap->get_status = eap_peap_get_status; - eap->has_reauth_data = eap_peap_has_reauth_data; - eap->deinit_for_reauth = eap_peap_deinit_for_reauth; - eap->init_for_reauth = eap_peap_init_for_reauth; - - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; -} |