📄 eap_peap.c
字号:
/* * EAP peer method: 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 "crypto/sha1.h"#include "eap_i.h"#include "eap_tls_common.h"#include "eap_config.h"#include "tls.h"#include "eap_common/eap_tlv_common.h"#include "eap_common/eap_peap_common.h"#include "tncc.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_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; struct wpabuf *pending_phase2_req; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; int crypto_binding_used; u8 ipmk[40]; u8 cmk[20]; int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) * is enabled. */};static int eap_peap_parse_phase1(struct eap_peap_data *data, const char *phase1){ const char *pos; pos = os_strstr(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(phase1, "peaplabel=1")) { data->force_new_label = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " "derivation"); } if (os_strstr(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(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(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 (os_strstr(phase1, "crypto_binding=0")) { data->crypto_binding = NO_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); } else if (os_strstr(phase1, "crypto_binding=1")) { data->crypto_binding = OPTIONAL_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); } else if (os_strstr(phase1, "crypto_binding=2")) { data->crypto_binding = REQUIRE_BINDING; wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); }#ifdef EAP_TNC if (os_strstr(phase1, "tnc=soh")) { data->soh = 1; wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH enabled"); }#endif /* EAP_TNC */ return 0;}static void * eap_peap_init(struct eap_sm *sm){ struct eap_peap_data *data; struct eap_peer_config *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; data->crypto_binding = OPTIONAL_BINDING; if (config && config->phase1 && eap_peap_parse_phase1(data, config->phase1) < 0) { eap_peap_deinit(sm, data); return NULL; } if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, &data->num_phase2_types) < 0) { eap_peap_deinit(sm, data); return NULL; } data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; if (eap_peer_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_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); wpabuf_free(data->pending_phase2_req); os_free(data);}/** * eap_tlv_build_nak - Build EAP-TLV NAK message * @id: EAP identifier for the header * @nak_type: TLV type (EAP_TLV_*) * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure * * This funtion builds an EAP-TLV NAK message. The caller is responsible for * freeing the returned buffer. */static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type){ struct wpabuf *msg; msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, EAP_CODE_RESPONSE, id); if (msg == NULL) return NULL; wpabuf_put_u8(msg, 0x80); /* Mandatory */ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); wpabuf_put_be16(msg, 6); /* Length */ wpabuf_put_be32(msg, 0); /* Vendor-Id */ wpabuf_put_be16(msg, nak_type); /* NAK-Type */ return msg;}static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, u8 *isk, size_t isk_len){ u8 *key; size_t key_len; os_memset(isk, 0, isk_len); if (data->phase2_method == NULL || data->phase2_priv == NULL || data->phase2_method->isKeyAvailable == NULL || data->phase2_method->getKey == NULL) return 0; if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || (key = data->phase2_method->getKey(sm, data->phase2_priv, &key_len)) == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " "from Phase 2"); return -1; } if (key_len == 32 && data->phase2_method->vendor == EAP_VENDOR_IETF && data->phase2_method->method == EAP_TYPE_MSCHAPV2) { /* * Microsoft uses reverse order for MS-MPPE keys in * EAP-PEAP when compared to EAP-FAST derivation of * ISK. Swap the keys here to get the correct ISK for * EAP-PEAPv0 cryptobinding. */ u8 tmp[16]; os_memcpy(tmp, key, 16); os_memcpy(key, key + 16, 16); os_memcpy(key + 16, tmp, 16); } if (key_len > isk_len) key_len = isk_len; os_memcpy(isk, key, key_len); os_free(key); return 0;}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 = data->key_data; if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) return -1; 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)); /* 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 int eap_tlv_add_cryptobinding(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *buf){ u8 *mac; u8 eap_type = EAP_TYPE_PEAP; const u8 *addr[2]; size_t len[2]; u16 tlv_type; u8 binding_nonce[32]; /* FIX: should binding_nonce be copied from request? */ if (os_get_random(binding_nonce, 32)) return -1; /* 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->peap_version); /* RecvVersion */ wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ wpabuf_put_data(buf, 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_used = 1; return 0;}/** * eap_tlv_build_result - Build EAP-TLV Result message * @id: EAP identifier for the header * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure * * This funtion builds an EAP-TLV Result message. The caller is responsible for * freeing the returned buffer. */static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, struct eap_peap_data *data, int crypto_tlv_used, int id, u16 status){ struct wpabuf *msg; size_t len; if (data->crypto_binding == NO_BINDING) crypto_tlv_used = 0; len = 6; if (crypto_tlv_used) len += 60; /* Cryptobinding TLV */ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, EAP_CODE_RESPONSE, id); if (msg == NULL) return NULL; wpabuf_put_u8(msg, 0x80); /* Mandatory */ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); wpabuf_put_be16(msg, 2); /* Length */ wpabuf_put_be16(msg, status); /* Status */ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { wpabuf_free(msg); return NULL; } return msg;}static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, struct eap_peap_data *data, const u8 *crypto_tlv, size_t crypto_tlv_len){ u8 buf[61], mac[SHA1_MAC_LEN]; const u8 *pos; if (eap_peap_derive_cmk(sm, data) < 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); return -1; } if (crypto_tlv_len != 4 + 56) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " "length %d", (int) crypto_tlv_len); return -1; } pos = crypto_tlv; pos += 4; /* TLV header */ if (pos[1] != data->peap_version) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " "mismatch (was %d; expected %d)", pos[1], data->peap_version); return -1; } if (pos[3] != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " "SubType %d", pos[3]); return -1; } pos += 4; pos += 32; /* Nonce */ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ os_memcpy(buf, crypto_tlv, 60); os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ buf[60] = EAP_TYPE_PEAP; hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -