📄 eap_peap.c
字号:
/* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, 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 "sha1.h"#include "eap_i.h"#include "eap_tls_common.h"#include "eap_common/eap_tlv_common.h"#include "eap_common/eap_peap_common.h"#include "tls.h"#include "tncs.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-10.txt */#define EAP_PEAP_VERSION 1static void eap_peap_reset(struct eap_sm *sm, void *priv);struct eap_peap_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID, PHASE2_METHOD, PHASE2_SOH, PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; int peap_version; int recv_version; const struct eap_method *phase2_method; void *phase2_priv; int force_version; struct wpabuf *pending_phase2_resp; enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; int crypto_binding_sent; int crypto_binding_used; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; u8 binding_nonce[32]; u8 ipmk[40]; u8 cmk[20]; u8 *phase2_key; size_t phase2_key_len; struct wpabuf *soh_response;};static const char * eap_peap_state_txt(int state){ switch (state) { case START: return "START"; case PHASE1: return "PHASE1"; case PHASE1_ID2: return "PHASE1_ID2"; case PHASE2_START: return "PHASE2_START"; case PHASE2_ID: return "PHASE2_ID"; case PHASE2_METHOD: return "PHASE2_METHOD"; case PHASE2_SOH: return "PHASE2_SOH"; case PHASE2_TLV: return "PHASE2_TLV"; case SUCCESS_REQ: return "SUCCESS_REQ"; case FAILURE_REQ: return "FAILURE_REQ"; case SUCCESS: return "SUCCESS"; case FAILURE: return "FAILURE"; default: return "Unknown?!"; }}static void eap_peap_state(struct eap_peap_data *data, int state){ wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", eap_peap_state_txt(data->state), eap_peap_state_txt(state)); data->state = state;}static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf){ struct wpabuf *e; struct eap_tlv_hdr *tlv; if (buf == NULL) return NULL; /* Encapsulate EAP packet in EAP-Payload TLV */ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); if (e == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " "for TLV encapsulation"); wpabuf_free(buf); return NULL; } tlv = wpabuf_put(e, sizeof(*tlv)); tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV); tlv->length = host_to_be16(wpabuf_len(buf)); wpabuf_put_buf(e, buf); wpabuf_free(buf); return e;}static void eap_peap_req_success(struct eap_sm *sm, struct eap_peap_data *data){ if (data->state == FAILURE || data->state == FAILURE_REQ) { eap_peap_state(data, FAILURE); return; } if (data->peap_version == 0) { data->tlv_request = TLV_REQ_SUCCESS; eap_peap_state(data, PHASE2_TLV); } else { eap_peap_state(data, SUCCESS_REQ); }}static void eap_peap_req_failure(struct eap_sm *sm, struct eap_peap_data *data){ if (data->state == FAILURE || data->state == FAILURE_REQ || data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) { eap_peap_state(data, FAILURE); return; } if (data->peap_version == 0) { data->tlv_request = TLV_REQ_FAILURE; eap_peap_state(data, PHASE2_TLV); } else { eap_peap_state(data, FAILURE_REQ); }}static void * eap_peap_init(struct eap_sm *sm){ struct eap_peap_data *data; data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->peap_version = EAP_PEAP_VERSION; data->force_version = -1; if (sm->user && sm->user->force_version >= 0) { data->force_version = sm->user->force_version; wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", data->force_version); data->peap_version = data->force_version; } data->state = START; data->crypto_binding = OPTIONAL_BINDING; if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_reset(sm, data); return NULL; } return data;}static void eap_peap_reset(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->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); wpabuf_free(data->pending_phase2_resp); os_free(data->phase2_key); wpabuf_free(data->soh_response); os_free(data);}static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data, u8 id){ struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" " request"); eap_peap_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version); eap_peap_state(data, PHASE1); return req;}static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, struct eap_peap_data *data, u8 id){ struct wpabuf *buf, *encr_req; const u8 *req; size_t req_len; if (data->phase2_method == NULL || data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready"); return NULL; } buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); if (data->peap_version >= 2 && buf) buf = eap_peapv2_tlv_eap_payload(buf); if (buf == NULL) return NULL; req = wpabuf_head(buf); req_len = wpabuf_len(buf); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", req, req_len); if (data->peap_version == 0 && data->phase2_method->method != EAP_TYPE_TLV) { req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); } encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); wpabuf_free(buf); return encr_req;}#ifdef EAP_TNCstatic struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, u8 id){ struct wpabuf *buf1, *buf, *encr_req; const u8 *req; size_t req_len; buf1 = tncs_build_soh_request(); if (buf1 == NULL) return NULL; buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1), EAP_CODE_REQUEST, id); if (buf == NULL) { wpabuf_free(buf1); return NULL; } wpabuf_put_buf(buf, buf1); wpabuf_free(buf1); req = wpabuf_head(buf); req_len = wpabuf_len(buf); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data", req, req_len); req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); wpabuf_free(buf); return encr_req;}#endif /* EAP_TNC */static void eap_peap_get_isk(struct eap_peap_data *data, u8 *isk, size_t isk_len){ size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_key == NULL) return; key_len = data->phase2_key_len; if (key_len > isk_len) key_len = isk_len; os_memcpy(isk, data->phase2_key, key_len);}static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data){ u8 *tk; u8 isk[32], imck[60]; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); eap_peap_get_isk(data, isk, sizeof(isk)); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); /* * IPMK Seed = "Inner Methods Compound Keys" | ISK * TempKey = First 40 octets of TK * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); os_free(tk); /* TODO: fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, imck, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); return 0;}static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, struct eap_peap_data *data, u8 id){ struct wpabuf *buf, *encr_req; size_t len; len = 6; /* Result TLV */ if (data->crypto_binding != NO_BINDING) len += 60; /* Cryptobinding TLV */#ifdef EAP_TNC if (data->soh_response) len += wpabuf_len(data->soh_response);#endif /* EAP_TNC */ buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, EAP_CODE_REQUEST, id); if (buf == NULL) return NULL; wpabuf_put_u8(buf, 0x80); /* Mandatory */ wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV); /* Length */ wpabuf_put_be16(buf, 2); /* Status */ wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && data->crypto_binding != NO_BINDING) { u8 *mac; u8 eap_type = EAP_TYPE_PEAP; const u8 *addr[2]; size_t len[2]; u16 tlv_type;#ifdef EAP_TNC if (data->soh_response) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " "Response TLV"); wpabuf_put_buf(buf, data->soh_response); wpabuf_free(data->soh_response); data->soh_response = NULL; }#endif /* EAP_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || os_get_random(data->binding_nonce, 32)) { wpabuf_free(buf); return NULL; } /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ addr[0] = wpabuf_put(buf, 0); len[0] = 60; addr[1] = &eap_type; len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; if (data->peap_version >= 2) tlv_type |= EAP_TLV_TYPE_MANDATORY; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); wpabuf_put_u8(buf, 0); /* Reserved */ wpabuf_put_u8(buf, data->peap_version); /* Version */ wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ mac = wpabuf_put(buf, 20); /* Compound_MAC */ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", addr[0], len[0]); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", addr[1], len[1]); hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); data->crypto_binding_sent = 1; } wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); encr_req = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return encr_req;}static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, struct eap_peap_data *data, u8 id, int success){ struct wpabuf *encr_req; size_t req_len; struct eap_hdr *hdr; req_len = sizeof(*hdr); hdr = os_zalloc(req_len); if (hdr == NULL) return NULL; hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; hdr->identifier = id; hdr->length = host_to_be16(req_len); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", (u8 *) hdr, req_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -