📄 eap_peap.c
字号:
/* * WPA Supplicant / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 <stdlib.h>#include <stdio.h>#include <string.h>#include "common.h"#include "eap_i.h"#include "eap_tls_common.h"#include "wpa_supplicant.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 1static 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; u8 phase2_type; u8 *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 = malloc(sizeof(*data)); if (data == NULL) return NULL; sm->peap_done = FALSE; memset(data, 0, sizeof(*data)); data->peap_version = EAP_PEAP_VERSION; data->force_peap_version = -1; data->peap_outer_success = 2; if (config && config->phase1) { char *pos = 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 (strstr(config->phase1, "peaplabel=1")) { data->force_new_label = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for " "key derivation"); } if (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 (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 (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; u8 method, *methods = NULL, *_methods; size_t num_methods = 0; start = buf = strdup(config->phase2); if (buf == NULL) { eap_peap_deinit(sm, data); return NULL; } while (start && *start != '\0') { pos = strstr(start, "auth="); if (pos == NULL) break; if (start != pos && *(pos - 1) != ' ') { start = pos + 5; continue; } start = pos + 5; pos = strchr(start, ' '); if (pos) *pos++ = '\0'; method = eap_get_phase2_type(start); if (method == EAP_TYPE_NONE) { wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported " "Phase2 method '%s'", start); } else { num_methods++; _methods = realloc(methods, num_methods); if (_methods == NULL) { free(methods); free(buf); eap_peap_deinit(sm, data); return NULL; } methods = _methods; methods[num_methods - 1] = method; } start = pos; } 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", data->phase2_types, data->num_phase2_types); data->phase2_type = 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); free(data->phase2_types); eap_tls_ssl_deinit(sm, &data->ssl); free(data->key_data); free(data->pending_phase2_req); 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 = 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"); 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_sm *sm, 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); wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types", data->phase2_types, data->num_phase2_types); *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types; *resp = malloc(*resp_len); if (*resp == NULL) return -1; resp_hdr = (struct eap_hdr *) (*resp); resp_hdr->code = EAP_CODE_RESPONSE; resp_hdr->identifier = hdr->identifier; resp_hdr->length = host_to_be16(*resp_len); pos = (u8 *) (resp_hdr + 1); *pos++ = EAP_TYPE_NAK; memcpy(pos, data->phase2_types, data->num_phase2_types); return 0;}static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, const struct eap_hdr *req, 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, req->identifier, resp_len, 1); break; case EAP_TYPE_TLV: memset(&iret, 0, sizeof(iret)); if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) { 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 == EAP_TYPE_NONE) { int i; for (i = 0; i < data->num_phase2_types; i++) { if (data->phase2_types[i] != *pos) continue; data->phase2_type = *pos; wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " "Phase 2 EAP method %d", data->phase2_type); break; } } if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) { if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len)) return -1; return 0; } if (data->phase2_priv == NULL) { data->phase2_method = eap_sm_get_eap_methods(*pos); 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; } 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_success = 1; } break; } if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password)) { free(data->pending_phase2_req); data->pending_phase2_req = malloc(len); if (data->pending_phase2_req) { 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 buf_len, len_decrypted, len, skip_change = 0; struct eap_hdr *hdr, *rhdr; u8 *resp = NULL; size_t resp_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. */ 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -