xref: /freebsd/contrib/wpa/src/eap_peer/eap_peap.c (revision 325151a3)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
339beb93cSSam Leffler  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "crypto/sha1.h"
13e28a4053SRui Paulo #include "crypto/tls.h"
14e28a4053SRui Paulo #include "eap_common/eap_tlv_common.h"
15e28a4053SRui Paulo #include "eap_common/eap_peap_common.h"
1639beb93cSSam Leffler #include "eap_i.h"
1739beb93cSSam Leffler #include "eap_tls_common.h"
1839beb93cSSam Leffler #include "eap_config.h"
1939beb93cSSam Leffler #include "tncc.h"
2039beb93cSSam Leffler 
2139beb93cSSam Leffler 
2239beb93cSSam Leffler /* Maximum supported PEAP version
2339beb93cSSam Leffler  * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
2439beb93cSSam Leffler  * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
2539beb93cSSam Leffler  */
2639beb93cSSam Leffler #define EAP_PEAP_VERSION 1
2739beb93cSSam Leffler 
2839beb93cSSam Leffler 
2939beb93cSSam Leffler static void eap_peap_deinit(struct eap_sm *sm, void *priv);
3039beb93cSSam Leffler 
3139beb93cSSam Leffler 
3239beb93cSSam Leffler struct eap_peap_data {
3339beb93cSSam Leffler 	struct eap_ssl_data ssl;
3439beb93cSSam Leffler 
3539beb93cSSam Leffler 	int peap_version, force_peap_version, force_new_label;
3639beb93cSSam Leffler 
3739beb93cSSam Leffler 	const struct eap_method *phase2_method;
3839beb93cSSam Leffler 	void *phase2_priv;
3939beb93cSSam Leffler 	int phase2_success;
4039beb93cSSam Leffler 	int phase2_eap_success;
4139beb93cSSam Leffler 	int phase2_eap_started;
4239beb93cSSam Leffler 
4339beb93cSSam Leffler 	struct eap_method_type phase2_type;
4439beb93cSSam Leffler 	struct eap_method_type *phase2_types;
4539beb93cSSam Leffler 	size_t num_phase2_types;
4639beb93cSSam Leffler 
4739beb93cSSam Leffler 	int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
4839beb93cSSam Leffler 				 * EAP-Success
4939beb93cSSam Leffler 				 * 1 = reply with tunneled EAP-Success to inner
5039beb93cSSam Leffler 				 * EAP-Success and expect AS to send outer
5139beb93cSSam Leffler 				 * (unencrypted) EAP-Success after this
5239beb93cSSam Leffler 				 * 2 = reply with PEAP/TLS ACK to inner
5339beb93cSSam Leffler 				 * EAP-Success and expect AS to send outer
5439beb93cSSam Leffler 				 * (unencrypted) EAP-Success after this */
5539beb93cSSam Leffler 	int resuming; /* starting a resumed session */
5639beb93cSSam Leffler 	int reauth; /* reauthentication */
5739beb93cSSam Leffler 	u8 *key_data;
585b9c547cSRui Paulo 	u8 *session_id;
595b9c547cSRui Paulo 	size_t id_len;
6039beb93cSSam Leffler 
6139beb93cSSam Leffler 	struct wpabuf *pending_phase2_req;
6239beb93cSSam Leffler 	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
6339beb93cSSam Leffler 	int crypto_binding_used;
6439beb93cSSam Leffler 	u8 binding_nonce[32];
6539beb93cSSam Leffler 	u8 ipmk[40];
6639beb93cSSam Leffler 	u8 cmk[20];
6739beb93cSSam Leffler 	int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
6839beb93cSSam Leffler 		  * is enabled. */
6939beb93cSSam Leffler };
7039beb93cSSam Leffler 
7139beb93cSSam Leffler 
7239beb93cSSam Leffler static int eap_peap_parse_phase1(struct eap_peap_data *data,
7339beb93cSSam Leffler 				 const char *phase1)
7439beb93cSSam Leffler {
7539beb93cSSam Leffler 	const char *pos;
7639beb93cSSam Leffler 
7739beb93cSSam Leffler 	pos = os_strstr(phase1, "peapver=");
7839beb93cSSam Leffler 	if (pos) {
7939beb93cSSam Leffler 		data->force_peap_version = atoi(pos + 8);
8039beb93cSSam Leffler 		data->peap_version = data->force_peap_version;
8139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
8239beb93cSSam Leffler 			   data->force_peap_version);
8339beb93cSSam Leffler 	}
8439beb93cSSam Leffler 
8539beb93cSSam Leffler 	if (os_strstr(phase1, "peaplabel=1")) {
8639beb93cSSam Leffler 		data->force_new_label = 1;
8739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
8839beb93cSSam Leffler 			   "derivation");
8939beb93cSSam Leffler 	}
9039beb93cSSam Leffler 
9139beb93cSSam Leffler 	if (os_strstr(phase1, "peap_outer_success=0")) {
9239beb93cSSam Leffler 		data->peap_outer_success = 0;
9339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
9439beb93cSSam Leffler 			   "tunneled EAP-Success");
9539beb93cSSam Leffler 	} else if (os_strstr(phase1, "peap_outer_success=1")) {
9639beb93cSSam Leffler 		data->peap_outer_success = 1;
9739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
9839beb93cSSam Leffler 			   "after receiving tunneled EAP-Success");
9939beb93cSSam Leffler 	} else if (os_strstr(phase1, "peap_outer_success=2")) {
10039beb93cSSam Leffler 		data->peap_outer_success = 2;
10139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
10239beb93cSSam Leffler 			   "receiving tunneled EAP-Success");
10339beb93cSSam Leffler 	}
10439beb93cSSam Leffler 
10539beb93cSSam Leffler 	if (os_strstr(phase1, "crypto_binding=0")) {
10639beb93cSSam Leffler 		data->crypto_binding = NO_BINDING;
10739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
10839beb93cSSam Leffler 	} else if (os_strstr(phase1, "crypto_binding=1")) {
10939beb93cSSam Leffler 		data->crypto_binding = OPTIONAL_BINDING;
11039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
11139beb93cSSam Leffler 	} else if (os_strstr(phase1, "crypto_binding=2")) {
11239beb93cSSam Leffler 		data->crypto_binding = REQUIRE_BINDING;
11339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
11439beb93cSSam Leffler 	}
11539beb93cSSam Leffler 
11639beb93cSSam Leffler #ifdef EAP_TNC
11739beb93cSSam Leffler 	if (os_strstr(phase1, "tnc=soh2")) {
11839beb93cSSam Leffler 		data->soh = 2;
11939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
12039beb93cSSam Leffler 	} else if (os_strstr(phase1, "tnc=soh1")) {
12139beb93cSSam Leffler 		data->soh = 1;
12239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
12339beb93cSSam Leffler 	} else if (os_strstr(phase1, "tnc=soh")) {
12439beb93cSSam Leffler 		data->soh = 2;
12539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
12639beb93cSSam Leffler 	}
12739beb93cSSam Leffler #endif /* EAP_TNC */
12839beb93cSSam Leffler 
12939beb93cSSam Leffler 	return 0;
13039beb93cSSam Leffler }
13139beb93cSSam Leffler 
13239beb93cSSam Leffler 
13339beb93cSSam Leffler static void * eap_peap_init(struct eap_sm *sm)
13439beb93cSSam Leffler {
13539beb93cSSam Leffler 	struct eap_peap_data *data;
13639beb93cSSam Leffler 	struct eap_peer_config *config = eap_get_config(sm);
13739beb93cSSam Leffler 
13839beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
13939beb93cSSam Leffler 	if (data == NULL)
14039beb93cSSam Leffler 		return NULL;
14139beb93cSSam Leffler 	sm->peap_done = FALSE;
14239beb93cSSam Leffler 	data->peap_version = EAP_PEAP_VERSION;
14339beb93cSSam Leffler 	data->force_peap_version = -1;
14439beb93cSSam Leffler 	data->peap_outer_success = 2;
14539beb93cSSam Leffler 	data->crypto_binding = OPTIONAL_BINDING;
14639beb93cSSam Leffler 
14739beb93cSSam Leffler 	if (config && config->phase1 &&
14839beb93cSSam Leffler 	    eap_peap_parse_phase1(data, config->phase1) < 0) {
14939beb93cSSam Leffler 		eap_peap_deinit(sm, data);
15039beb93cSSam Leffler 		return NULL;
15139beb93cSSam Leffler 	}
15239beb93cSSam Leffler 
15339beb93cSSam Leffler 	if (eap_peer_select_phase2_methods(config, "auth=",
15439beb93cSSam Leffler 					   &data->phase2_types,
15539beb93cSSam Leffler 					   &data->num_phase2_types) < 0) {
15639beb93cSSam Leffler 		eap_peap_deinit(sm, data);
15739beb93cSSam Leffler 		return NULL;
15839beb93cSSam Leffler 	}
15939beb93cSSam Leffler 
16039beb93cSSam Leffler 	data->phase2_type.vendor = EAP_VENDOR_IETF;
16139beb93cSSam Leffler 	data->phase2_type.method = EAP_TYPE_NONE;
16239beb93cSSam Leffler 
163f05cddf9SRui Paulo 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
16439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
16539beb93cSSam Leffler 		eap_peap_deinit(sm, data);
16639beb93cSSam Leffler 		return NULL;
16739beb93cSSam Leffler 	}
16839beb93cSSam Leffler 
16939beb93cSSam Leffler 	return data;
17039beb93cSSam Leffler }
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 
1735b9c547cSRui Paulo static void eap_peap_free_key(struct eap_peap_data *data)
1745b9c547cSRui Paulo {
1755b9c547cSRui Paulo 	if (data->key_data) {
1765b9c547cSRui Paulo 		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
1775b9c547cSRui Paulo 		data->key_data = NULL;
1785b9c547cSRui Paulo 	}
1795b9c547cSRui Paulo }
1805b9c547cSRui Paulo 
1815b9c547cSRui Paulo 
18239beb93cSSam Leffler static void eap_peap_deinit(struct eap_sm *sm, void *priv)
18339beb93cSSam Leffler {
18439beb93cSSam Leffler 	struct eap_peap_data *data = priv;
18539beb93cSSam Leffler 	if (data == NULL)
18639beb93cSSam Leffler 		return;
18739beb93cSSam Leffler 	if (data->phase2_priv && data->phase2_method)
18839beb93cSSam Leffler 		data->phase2_method->deinit(sm, data->phase2_priv);
18939beb93cSSam Leffler 	os_free(data->phase2_types);
19039beb93cSSam Leffler 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
1915b9c547cSRui Paulo 	eap_peap_free_key(data);
1925b9c547cSRui Paulo 	os_free(data->session_id);
19339beb93cSSam Leffler 	wpabuf_free(data->pending_phase2_req);
19439beb93cSSam Leffler 	os_free(data);
19539beb93cSSam Leffler }
19639beb93cSSam Leffler 
19739beb93cSSam Leffler 
19839beb93cSSam Leffler /**
19939beb93cSSam Leffler  * eap_tlv_build_nak - Build EAP-TLV NAK message
20039beb93cSSam Leffler  * @id: EAP identifier for the header
20139beb93cSSam Leffler  * @nak_type: TLV type (EAP_TLV_*)
20239beb93cSSam Leffler  * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
20339beb93cSSam Leffler  *
204f05cddf9SRui Paulo  * This function builds an EAP-TLV NAK message. The caller is responsible for
20539beb93cSSam Leffler  * freeing the returned buffer.
20639beb93cSSam Leffler  */
20739beb93cSSam Leffler static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
20839beb93cSSam Leffler {
20939beb93cSSam Leffler 	struct wpabuf *msg;
21039beb93cSSam Leffler 
21139beb93cSSam Leffler 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
21239beb93cSSam Leffler 			    EAP_CODE_RESPONSE, id);
21339beb93cSSam Leffler 	if (msg == NULL)
21439beb93cSSam Leffler 		return NULL;
21539beb93cSSam Leffler 
21639beb93cSSam Leffler 	wpabuf_put_u8(msg, 0x80); /* Mandatory */
21739beb93cSSam Leffler 	wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
21839beb93cSSam Leffler 	wpabuf_put_be16(msg, 6); /* Length */
21939beb93cSSam Leffler 	wpabuf_put_be32(msg, 0); /* Vendor-Id */
22039beb93cSSam Leffler 	wpabuf_put_be16(msg, nak_type); /* NAK-Type */
22139beb93cSSam Leffler 
22239beb93cSSam Leffler 	return msg;
22339beb93cSSam Leffler }
22439beb93cSSam Leffler 
22539beb93cSSam Leffler 
22639beb93cSSam Leffler static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
22739beb93cSSam Leffler 			    u8 *isk, size_t isk_len)
22839beb93cSSam Leffler {
22939beb93cSSam Leffler 	u8 *key;
23039beb93cSSam Leffler 	size_t key_len;
23139beb93cSSam Leffler 
23239beb93cSSam Leffler 	os_memset(isk, 0, isk_len);
23339beb93cSSam Leffler 	if (data->phase2_method == NULL || data->phase2_priv == NULL ||
23439beb93cSSam Leffler 	    data->phase2_method->isKeyAvailable == NULL ||
23539beb93cSSam Leffler 	    data->phase2_method->getKey == NULL)
23639beb93cSSam Leffler 		return 0;
23739beb93cSSam Leffler 
23839beb93cSSam Leffler 	if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
23939beb93cSSam Leffler 	    (key = data->phase2_method->getKey(sm, data->phase2_priv,
24039beb93cSSam Leffler 					       &key_len)) == NULL) {
24139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
24239beb93cSSam Leffler 			   "from Phase 2");
24339beb93cSSam Leffler 		return -1;
24439beb93cSSam Leffler 	}
24539beb93cSSam Leffler 
24639beb93cSSam Leffler 	if (key_len > isk_len)
24739beb93cSSam Leffler 		key_len = isk_len;
24839beb93cSSam Leffler 	os_memcpy(isk, key, key_len);
24939beb93cSSam Leffler 	os_free(key);
25039beb93cSSam Leffler 
25139beb93cSSam Leffler 	return 0;
25239beb93cSSam Leffler }
25339beb93cSSam Leffler 
25439beb93cSSam Leffler 
25539beb93cSSam Leffler static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
25639beb93cSSam Leffler {
25739beb93cSSam Leffler 	u8 *tk;
25839beb93cSSam Leffler 	u8 isk[32], imck[60];
25939beb93cSSam Leffler 
26039beb93cSSam Leffler 	/*
26139beb93cSSam Leffler 	 * Tunnel key (TK) is the first 60 octets of the key generated by
26239beb93cSSam Leffler 	 * phase 1 of PEAP (based on TLS).
26339beb93cSSam Leffler 	 */
26439beb93cSSam Leffler 	tk = data->key_data;
26539beb93cSSam Leffler 	if (tk == NULL)
26639beb93cSSam Leffler 		return -1;
26739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
26839beb93cSSam Leffler 
26939beb93cSSam Leffler 	if (data->reauth &&
27039beb93cSSam Leffler 	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
27139beb93cSSam Leffler 		/* Fast-connect: IPMK|CMK = TK */
27239beb93cSSam Leffler 		os_memcpy(data->ipmk, tk, 40);
27339beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
27439beb93cSSam Leffler 				data->ipmk, 40);
27539beb93cSSam Leffler 		os_memcpy(data->cmk, tk + 40, 20);
27639beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
27739beb93cSSam Leffler 				data->cmk, 20);
27839beb93cSSam Leffler 		return 0;
27939beb93cSSam Leffler 	}
28039beb93cSSam Leffler 
28139beb93cSSam Leffler 	if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
28239beb93cSSam Leffler 		return -1;
28339beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
28439beb93cSSam Leffler 
28539beb93cSSam Leffler 	/*
28639beb93cSSam Leffler 	 * IPMK Seed = "Inner Methods Compound Keys" | ISK
28739beb93cSSam Leffler 	 * TempKey = First 40 octets of TK
28839beb93cSSam Leffler 	 * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
28939beb93cSSam Leffler 	 * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
29039beb93cSSam Leffler 	 * in the end of the label just before ISK; is that just a typo?)
29139beb93cSSam Leffler 	 */
29239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
293f05cddf9SRui Paulo 	if (peap_prfplus(data->peap_version, tk, 40,
294f05cddf9SRui Paulo 			 "Inner Methods Compound Keys",
295f05cddf9SRui Paulo 			 isk, sizeof(isk), imck, sizeof(imck)) < 0)
296f05cddf9SRui Paulo 		return -1;
29739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
29839beb93cSSam Leffler 			imck, sizeof(imck));
29939beb93cSSam Leffler 
30039beb93cSSam Leffler 	os_memcpy(data->ipmk, imck, 40);
30139beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
30239beb93cSSam Leffler 	os_memcpy(data->cmk, imck + 40, 20);
30339beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
30439beb93cSSam Leffler 
30539beb93cSSam Leffler 	return 0;
30639beb93cSSam Leffler }
30739beb93cSSam Leffler 
30839beb93cSSam Leffler 
30939beb93cSSam Leffler static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
31039beb93cSSam Leffler 				     struct eap_peap_data *data,
31139beb93cSSam Leffler 				     struct wpabuf *buf)
31239beb93cSSam Leffler {
31339beb93cSSam Leffler 	u8 *mac;
31439beb93cSSam Leffler 	u8 eap_type = EAP_TYPE_PEAP;
31539beb93cSSam Leffler 	const u8 *addr[2];
31639beb93cSSam Leffler 	size_t len[2];
31739beb93cSSam Leffler 	u16 tlv_type;
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
32039beb93cSSam Leffler 	addr[0] = wpabuf_put(buf, 0);
32139beb93cSSam Leffler 	len[0] = 60;
32239beb93cSSam Leffler 	addr[1] = &eap_type;
32339beb93cSSam Leffler 	len[1] = 1;
32439beb93cSSam Leffler 
32539beb93cSSam Leffler 	tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
32639beb93cSSam Leffler 	wpabuf_put_be16(buf, tlv_type);
32739beb93cSSam Leffler 	wpabuf_put_be16(buf, 56);
32839beb93cSSam Leffler 
32939beb93cSSam Leffler 	wpabuf_put_u8(buf, 0); /* Reserved */
33039beb93cSSam Leffler 	wpabuf_put_u8(buf, data->peap_version); /* Version */
33139beb93cSSam Leffler 	wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
33239beb93cSSam Leffler 	wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
33339beb93cSSam Leffler 	wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
33439beb93cSSam Leffler 	mac = wpabuf_put(buf, 20); /* Compound_MAC */
33539beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
33639beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
33739beb93cSSam Leffler 		    addr[0], len[0]);
33839beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
33939beb93cSSam Leffler 		    addr[1], len[1]);
34039beb93cSSam Leffler 	hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
34139beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
34239beb93cSSam Leffler 	data->crypto_binding_used = 1;
34339beb93cSSam Leffler 
34439beb93cSSam Leffler 	return 0;
34539beb93cSSam Leffler }
34639beb93cSSam Leffler 
34739beb93cSSam Leffler 
34839beb93cSSam Leffler /**
34939beb93cSSam Leffler  * eap_tlv_build_result - Build EAP-TLV Result message
35039beb93cSSam Leffler  * @id: EAP identifier for the header
35139beb93cSSam Leffler  * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
35239beb93cSSam Leffler  * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
35339beb93cSSam Leffler  *
354f05cddf9SRui Paulo  * This function builds an EAP-TLV Result message. The caller is responsible
355f05cddf9SRui Paulo  * for freeing the returned buffer.
35639beb93cSSam Leffler  */
35739beb93cSSam Leffler static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
35839beb93cSSam Leffler 					    struct eap_peap_data *data,
35939beb93cSSam Leffler 					    int crypto_tlv_used,
36039beb93cSSam Leffler 					    int id, u16 status)
36139beb93cSSam Leffler {
36239beb93cSSam Leffler 	struct wpabuf *msg;
36339beb93cSSam Leffler 	size_t len;
36439beb93cSSam Leffler 
36539beb93cSSam Leffler 	if (data->crypto_binding == NO_BINDING)
36639beb93cSSam Leffler 		crypto_tlv_used = 0;
36739beb93cSSam Leffler 
36839beb93cSSam Leffler 	len = 6;
36939beb93cSSam Leffler 	if (crypto_tlv_used)
37039beb93cSSam Leffler 		len += 60; /* Cryptobinding TLV */
37139beb93cSSam Leffler 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
37239beb93cSSam Leffler 			    EAP_CODE_RESPONSE, id);
37339beb93cSSam Leffler 	if (msg == NULL)
37439beb93cSSam Leffler 		return NULL;
37539beb93cSSam Leffler 
37639beb93cSSam Leffler 	wpabuf_put_u8(msg, 0x80); /* Mandatory */
37739beb93cSSam Leffler 	wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
37839beb93cSSam Leffler 	wpabuf_put_be16(msg, 2); /* Length */
37939beb93cSSam Leffler 	wpabuf_put_be16(msg, status); /* Status */
38039beb93cSSam Leffler 
38139beb93cSSam Leffler 	if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
38239beb93cSSam Leffler 		wpabuf_free(msg);
38339beb93cSSam Leffler 		return NULL;
38439beb93cSSam Leffler 	}
38539beb93cSSam Leffler 
38639beb93cSSam Leffler 	return msg;
38739beb93cSSam Leffler }
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 
39039beb93cSSam Leffler static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
39139beb93cSSam Leffler 					  struct eap_peap_data *data,
39239beb93cSSam Leffler 					  const u8 *crypto_tlv,
39339beb93cSSam Leffler 					  size_t crypto_tlv_len)
39439beb93cSSam Leffler {
39539beb93cSSam Leffler 	u8 buf[61], mac[SHA1_MAC_LEN];
39639beb93cSSam Leffler 	const u8 *pos;
39739beb93cSSam Leffler 
39839beb93cSSam Leffler 	if (eap_peap_derive_cmk(sm, data) < 0) {
39939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
40039beb93cSSam Leffler 		return -1;
40139beb93cSSam Leffler 	}
40239beb93cSSam Leffler 
40339beb93cSSam Leffler 	if (crypto_tlv_len != 4 + 56) {
40439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
40539beb93cSSam Leffler 			   "length %d", (int) crypto_tlv_len);
40639beb93cSSam Leffler 		return -1;
40739beb93cSSam Leffler 	}
40839beb93cSSam Leffler 
40939beb93cSSam Leffler 	pos = crypto_tlv;
41039beb93cSSam Leffler 	pos += 4; /* TLV header */
41139beb93cSSam Leffler 	if (pos[1] != data->peap_version) {
41239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
41339beb93cSSam Leffler 			   "mismatch (was %d; expected %d)",
41439beb93cSSam Leffler 			   pos[1], data->peap_version);
41539beb93cSSam Leffler 		return -1;
41639beb93cSSam Leffler 	}
41739beb93cSSam Leffler 
41839beb93cSSam Leffler 	if (pos[3] != 0) {
41939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
42039beb93cSSam Leffler 			   "SubType %d", pos[3]);
42139beb93cSSam Leffler 		return -1;
42239beb93cSSam Leffler 	}
42339beb93cSSam Leffler 	pos += 4;
42439beb93cSSam Leffler 	os_memcpy(data->binding_nonce, pos, 32);
42539beb93cSSam Leffler 	pos += 32; /* Nonce */
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
42839beb93cSSam Leffler 	os_memcpy(buf, crypto_tlv, 60);
42939beb93cSSam Leffler 	os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
43039beb93cSSam Leffler 	buf[60] = EAP_TYPE_PEAP;
43139beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
43239beb93cSSam Leffler 		    buf, sizeof(buf));
43339beb93cSSam Leffler 	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
43439beb93cSSam Leffler 
4355b9c547cSRui Paulo 	if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
43639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
43739beb93cSSam Leffler 			   "cryptobinding TLV");
43839beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
43939beb93cSSam Leffler 			    pos, SHA1_MAC_LEN);
44039beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
44139beb93cSSam Leffler 			    mac, SHA1_MAC_LEN);
44239beb93cSSam Leffler 		return -1;
44339beb93cSSam Leffler 	}
44439beb93cSSam Leffler 
44539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
44639beb93cSSam Leffler 
44739beb93cSSam Leffler 	return 0;
44839beb93cSSam Leffler }
44939beb93cSSam Leffler 
45039beb93cSSam Leffler 
45139beb93cSSam Leffler /**
45239beb93cSSam Leffler  * eap_tlv_process - Process a received EAP-TLV message and generate a response
45339beb93cSSam Leffler  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
45439beb93cSSam Leffler  * @ret: Return values from EAP request validation and processing
45539beb93cSSam Leffler  * @req: EAP-TLV request to be processed. The caller must have validated that
45639beb93cSSam Leffler  * the buffer is large enough to contain full request (hdr->length bytes) and
45739beb93cSSam Leffler  * that the EAP type is EAP_TYPE_TLV.
45839beb93cSSam Leffler  * @resp: Buffer to return a pointer to the allocated response message. This
45939beb93cSSam Leffler  * field should be initialized to %NULL before the call. The value will be
46039beb93cSSam Leffler  * updated if a response message is generated. The caller is responsible for
46139beb93cSSam Leffler  * freeing the allocated message.
46239beb93cSSam Leffler  * @force_failure: Force negotiation to fail
46339beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
46439beb93cSSam Leffler  */
46539beb93cSSam Leffler static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
46639beb93cSSam Leffler 			   struct eap_method_ret *ret,
46739beb93cSSam Leffler 			   const struct wpabuf *req, struct wpabuf **resp,
46839beb93cSSam Leffler 			   int force_failure)
46939beb93cSSam Leffler {
47039beb93cSSam Leffler 	size_t left, tlv_len;
47139beb93cSSam Leffler 	const u8 *pos;
47239beb93cSSam Leffler 	const u8 *result_tlv = NULL, *crypto_tlv = NULL;
47339beb93cSSam Leffler 	size_t result_tlv_len = 0, crypto_tlv_len = 0;
47439beb93cSSam Leffler 	int tlv_type, mandatory;
47539beb93cSSam Leffler 
47639beb93cSSam Leffler 	/* Parse TLVs */
47739beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
47839beb93cSSam Leffler 	if (pos == NULL)
47939beb93cSSam Leffler 		return -1;
48039beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
48139beb93cSSam Leffler 	while (left >= 4) {
48239beb93cSSam Leffler 		mandatory = !!(pos[0] & 0x80);
48339beb93cSSam Leffler 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
48439beb93cSSam Leffler 		pos += 2;
48539beb93cSSam Leffler 		tlv_len = WPA_GET_BE16(pos);
48639beb93cSSam Leffler 		pos += 2;
48739beb93cSSam Leffler 		left -= 4;
48839beb93cSSam Leffler 		if (tlv_len > left) {
48939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
49039beb93cSSam Leffler 				   "(tlv_len=%lu left=%lu)",
49139beb93cSSam Leffler 				   (unsigned long) tlv_len,
49239beb93cSSam Leffler 				   (unsigned long) left);
49339beb93cSSam Leffler 			return -1;
49439beb93cSSam Leffler 		}
49539beb93cSSam Leffler 		switch (tlv_type) {
49639beb93cSSam Leffler 		case EAP_TLV_RESULT_TLV:
49739beb93cSSam Leffler 			result_tlv = pos;
49839beb93cSSam Leffler 			result_tlv_len = tlv_len;
49939beb93cSSam Leffler 			break;
50039beb93cSSam Leffler 		case EAP_TLV_CRYPTO_BINDING_TLV:
50139beb93cSSam Leffler 			crypto_tlv = pos;
50239beb93cSSam Leffler 			crypto_tlv_len = tlv_len;
50339beb93cSSam Leffler 			break;
50439beb93cSSam Leffler 		default:
50539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
50639beb93cSSam Leffler 				   "%d%s", tlv_type,
50739beb93cSSam Leffler 				   mandatory ? " (mandatory)" : "");
50839beb93cSSam Leffler 			if (mandatory) {
50939beb93cSSam Leffler 				/* NAK TLV and ignore all TLVs in this packet.
51039beb93cSSam Leffler 				 */
51139beb93cSSam Leffler 				*resp = eap_tlv_build_nak(eap_get_id(req),
51239beb93cSSam Leffler 							  tlv_type);
51339beb93cSSam Leffler 				return *resp == NULL ? -1 : 0;
51439beb93cSSam Leffler 			}
51539beb93cSSam Leffler 			/* Ignore this TLV, but process other TLVs */
51639beb93cSSam Leffler 			break;
51739beb93cSSam Leffler 		}
51839beb93cSSam Leffler 
51939beb93cSSam Leffler 		pos += tlv_len;
52039beb93cSSam Leffler 		left -= tlv_len;
52139beb93cSSam Leffler 	}
52239beb93cSSam Leffler 	if (left) {
52339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
52439beb93cSSam Leffler 			   "Request (left=%lu)", (unsigned long) left);
52539beb93cSSam Leffler 		return -1;
52639beb93cSSam Leffler 	}
52739beb93cSSam Leffler 
52839beb93cSSam Leffler 	/* Process supported TLVs */
52939beb93cSSam Leffler 	if (crypto_tlv && data->crypto_binding != NO_BINDING) {
53039beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
53139beb93cSSam Leffler 			    crypto_tlv, crypto_tlv_len);
53239beb93cSSam Leffler 		if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
53339beb93cSSam Leffler 						   crypto_tlv_len + 4) < 0) {
53439beb93cSSam Leffler 			if (result_tlv == NULL)
53539beb93cSSam Leffler 				return -1;
53639beb93cSSam Leffler 			force_failure = 1;
53739beb93cSSam Leffler 			crypto_tlv = NULL; /* do not include Cryptobinding TLV
53839beb93cSSam Leffler 					    * in response, if the received
53939beb93cSSam Leffler 					    * cryptobinding was invalid. */
54039beb93cSSam Leffler 		}
54139beb93cSSam Leffler 	} else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
54239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
54339beb93cSSam Leffler 		return -1;
54439beb93cSSam Leffler 	}
54539beb93cSSam Leffler 
54639beb93cSSam Leffler 	if (result_tlv) {
54739beb93cSSam Leffler 		int status, resp_status;
54839beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
54939beb93cSSam Leffler 			    result_tlv, result_tlv_len);
55039beb93cSSam Leffler 		if (result_tlv_len < 2) {
55139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
55239beb93cSSam Leffler 				   "(len=%lu)",
55339beb93cSSam Leffler 				   (unsigned long) result_tlv_len);
55439beb93cSSam Leffler 			return -1;
55539beb93cSSam Leffler 		}
55639beb93cSSam Leffler 		status = WPA_GET_BE16(result_tlv);
55739beb93cSSam Leffler 		if (status == EAP_TLV_RESULT_SUCCESS) {
55839beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
55939beb93cSSam Leffler 				   "- EAP-TLV/Phase2 Completed");
56039beb93cSSam Leffler 			if (force_failure) {
56139beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
56239beb93cSSam Leffler 					   " - force failed Phase 2");
56339beb93cSSam Leffler 				resp_status = EAP_TLV_RESULT_FAILURE;
56439beb93cSSam Leffler 				ret->decision = DECISION_FAIL;
56539beb93cSSam Leffler 			} else {
56639beb93cSSam Leffler 				resp_status = EAP_TLV_RESULT_SUCCESS;
56739beb93cSSam Leffler 				ret->decision = DECISION_UNCOND_SUCC;
56839beb93cSSam Leffler 			}
56939beb93cSSam Leffler 		} else if (status == EAP_TLV_RESULT_FAILURE) {
57039beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
57139beb93cSSam Leffler 			resp_status = EAP_TLV_RESULT_FAILURE;
57239beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
57339beb93cSSam Leffler 		} else {
57439beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
57539beb93cSSam Leffler 				   "Status %d", status);
57639beb93cSSam Leffler 			resp_status = EAP_TLV_RESULT_FAILURE;
57739beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
57839beb93cSSam Leffler 		}
57939beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
58039beb93cSSam Leffler 
58139beb93cSSam Leffler 		*resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
58239beb93cSSam Leffler 					     eap_get_id(req), resp_status);
58339beb93cSSam Leffler 	}
58439beb93cSSam Leffler 
58539beb93cSSam Leffler 	return 0;
58639beb93cSSam Leffler }
58739beb93cSSam Leffler 
58839beb93cSSam Leffler 
58939beb93cSSam Leffler static int eap_peap_phase2_request(struct eap_sm *sm,
59039beb93cSSam Leffler 				   struct eap_peap_data *data,
59139beb93cSSam Leffler 				   struct eap_method_ret *ret,
59239beb93cSSam Leffler 				   struct wpabuf *req,
59339beb93cSSam Leffler 				   struct wpabuf **resp)
59439beb93cSSam Leffler {
59539beb93cSSam Leffler 	struct eap_hdr *hdr = wpabuf_mhead(req);
59639beb93cSSam Leffler 	size_t len = be_to_host16(hdr->length);
59739beb93cSSam Leffler 	u8 *pos;
59839beb93cSSam Leffler 	struct eap_method_ret iret;
59939beb93cSSam Leffler 	struct eap_peer_config *config = eap_get_config(sm);
60039beb93cSSam Leffler 
60139beb93cSSam Leffler 	if (len <= sizeof(struct eap_hdr)) {
60239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: too short "
60339beb93cSSam Leffler 			   "Phase 2 request (len=%lu)", (unsigned long) len);
60439beb93cSSam Leffler 		return -1;
60539beb93cSSam Leffler 	}
60639beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
60739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
60839beb93cSSam Leffler 	switch (*pos) {
60939beb93cSSam Leffler 	case EAP_TYPE_IDENTITY:
61039beb93cSSam Leffler 		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
61139beb93cSSam Leffler 		break;
61239beb93cSSam Leffler 	case EAP_TYPE_TLV:
61339beb93cSSam Leffler 		os_memset(&iret, 0, sizeof(iret));
61439beb93cSSam Leffler 		if (eap_tlv_process(sm, data, &iret, req, resp,
61539beb93cSSam Leffler 				    data->phase2_eap_started &&
61639beb93cSSam Leffler 				    !data->phase2_eap_success)) {
61739beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
61839beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
61939beb93cSSam Leffler 			return -1;
62039beb93cSSam Leffler 		}
62139beb93cSSam Leffler 		if (iret.methodState == METHOD_DONE ||
62239beb93cSSam Leffler 		    iret.methodState == METHOD_MAY_CONT) {
62339beb93cSSam Leffler 			ret->methodState = iret.methodState;
62439beb93cSSam Leffler 			ret->decision = iret.decision;
62539beb93cSSam Leffler 			data->phase2_success = 1;
62639beb93cSSam Leffler 		}
62739beb93cSSam Leffler 		break;
62839beb93cSSam Leffler 	case EAP_TYPE_EXPANDED:
62939beb93cSSam Leffler #ifdef EAP_TNC
63039beb93cSSam Leffler 		if (data->soh) {
63139beb93cSSam Leffler 			const u8 *epos;
63239beb93cSSam Leffler 			size_t eleft;
63339beb93cSSam Leffler 
63439beb93cSSam Leffler 			epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
63539beb93cSSam Leffler 						req, &eleft);
63639beb93cSSam Leffler 			if (epos) {
63739beb93cSSam Leffler 				struct wpabuf *buf;
63839beb93cSSam Leffler 				wpa_printf(MSG_DEBUG,
63939beb93cSSam Leffler 					   "EAP-PEAP: SoH EAP Extensions");
64039beb93cSSam Leffler 				buf = tncc_process_soh_request(data->soh,
64139beb93cSSam Leffler 							       epos, eleft);
64239beb93cSSam Leffler 				if (buf) {
64339beb93cSSam Leffler 					*resp = eap_msg_alloc(
64439beb93cSSam Leffler 						EAP_VENDOR_MICROSOFT, 0x21,
64539beb93cSSam Leffler 						wpabuf_len(buf),
64639beb93cSSam Leffler 						EAP_CODE_RESPONSE,
64739beb93cSSam Leffler 						hdr->identifier);
64839beb93cSSam Leffler 					if (*resp == NULL) {
64939beb93cSSam Leffler 						ret->methodState = METHOD_DONE;
65039beb93cSSam Leffler 						ret->decision = DECISION_FAIL;
65139beb93cSSam Leffler 						return -1;
65239beb93cSSam Leffler 					}
65339beb93cSSam Leffler 					wpabuf_put_buf(*resp, buf);
65439beb93cSSam Leffler 					wpabuf_free(buf);
65539beb93cSSam Leffler 					break;
65639beb93cSSam Leffler 				}
65739beb93cSSam Leffler 			}
65839beb93cSSam Leffler 		}
65939beb93cSSam Leffler #endif /* EAP_TNC */
66039beb93cSSam Leffler 		/* fall through */
66139beb93cSSam Leffler 	default:
66239beb93cSSam Leffler 		if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
66339beb93cSSam Leffler 		    data->phase2_type.method == EAP_TYPE_NONE) {
66439beb93cSSam Leffler 			size_t i;
66539beb93cSSam Leffler 			for (i = 0; i < data->num_phase2_types; i++) {
66639beb93cSSam Leffler 				if (data->phase2_types[i].vendor !=
66739beb93cSSam Leffler 				    EAP_VENDOR_IETF ||
66839beb93cSSam Leffler 				    data->phase2_types[i].method != *pos)
66939beb93cSSam Leffler 					continue;
67039beb93cSSam Leffler 
67139beb93cSSam Leffler 				data->phase2_type.vendor =
67239beb93cSSam Leffler 					data->phase2_types[i].vendor;
67339beb93cSSam Leffler 				data->phase2_type.method =
67439beb93cSSam Leffler 					data->phase2_types[i].method;
67539beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
67639beb93cSSam Leffler 					   "Phase 2 EAP vendor %d method %d",
67739beb93cSSam Leffler 					   data->phase2_type.vendor,
67839beb93cSSam Leffler 					   data->phase2_type.method);
67939beb93cSSam Leffler 				break;
68039beb93cSSam Leffler 			}
68139beb93cSSam Leffler 		}
68239beb93cSSam Leffler 		if (*pos != data->phase2_type.method ||
68339beb93cSSam Leffler 		    *pos == EAP_TYPE_NONE) {
68439beb93cSSam Leffler 			if (eap_peer_tls_phase2_nak(data->phase2_types,
68539beb93cSSam Leffler 						    data->num_phase2_types,
68639beb93cSSam Leffler 						    hdr, resp))
68739beb93cSSam Leffler 				return -1;
68839beb93cSSam Leffler 			return 0;
68939beb93cSSam Leffler 		}
69039beb93cSSam Leffler 
69139beb93cSSam Leffler 		if (data->phase2_priv == NULL) {
69239beb93cSSam Leffler 			data->phase2_method = eap_peer_get_eap_method(
69339beb93cSSam Leffler 				data->phase2_type.vendor,
69439beb93cSSam Leffler 				data->phase2_type.method);
69539beb93cSSam Leffler 			if (data->phase2_method) {
69639beb93cSSam Leffler 				sm->init_phase2 = 1;
69739beb93cSSam Leffler 				data->phase2_priv =
69839beb93cSSam Leffler 					data->phase2_method->init(sm);
69939beb93cSSam Leffler 				sm->init_phase2 = 0;
70039beb93cSSam Leffler 			}
70139beb93cSSam Leffler 		}
70239beb93cSSam Leffler 		if (data->phase2_priv == NULL || data->phase2_method == NULL) {
70339beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
70439beb93cSSam Leffler 				   "Phase 2 EAP method %d", *pos);
70539beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
70639beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
70739beb93cSSam Leffler 			return -1;
70839beb93cSSam Leffler 		}
70939beb93cSSam Leffler 		data->phase2_eap_started = 1;
71039beb93cSSam Leffler 		os_memset(&iret, 0, sizeof(iret));
71139beb93cSSam Leffler 		*resp = data->phase2_method->process(sm, data->phase2_priv,
71239beb93cSSam Leffler 						     &iret, req);
71339beb93cSSam Leffler 		if ((iret.methodState == METHOD_DONE ||
71439beb93cSSam Leffler 		     iret.methodState == METHOD_MAY_CONT) &&
71539beb93cSSam Leffler 		    (iret.decision == DECISION_UNCOND_SUCC ||
71639beb93cSSam Leffler 		     iret.decision == DECISION_COND_SUCC)) {
71739beb93cSSam Leffler 			data->phase2_eap_success = 1;
71839beb93cSSam Leffler 			data->phase2_success = 1;
71939beb93cSSam Leffler 		}
72039beb93cSSam Leffler 		break;
72139beb93cSSam Leffler 	}
72239beb93cSSam Leffler 
72339beb93cSSam Leffler 	if (*resp == NULL &&
72439beb93cSSam Leffler 	    (config->pending_req_identity || config->pending_req_password ||
72539beb93cSSam Leffler 	     config->pending_req_otp || config->pending_req_new_password)) {
72639beb93cSSam Leffler 		wpabuf_free(data->pending_phase2_req);
72739beb93cSSam Leffler 		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
72839beb93cSSam Leffler 	}
72939beb93cSSam Leffler 
73039beb93cSSam Leffler 	return 0;
73139beb93cSSam Leffler }
73239beb93cSSam Leffler 
73339beb93cSSam Leffler 
73439beb93cSSam Leffler static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
73539beb93cSSam Leffler 			    struct eap_method_ret *ret,
73639beb93cSSam Leffler 			    const struct eap_hdr *req,
73739beb93cSSam Leffler 			    const struct wpabuf *in_data,
73839beb93cSSam Leffler 			    struct wpabuf **out_data)
73939beb93cSSam Leffler {
74039beb93cSSam Leffler 	struct wpabuf *in_decrypted = NULL;
74139beb93cSSam Leffler 	int res, skip_change = 0;
74239beb93cSSam Leffler 	struct eap_hdr *hdr, *rhdr;
74339beb93cSSam Leffler 	struct wpabuf *resp = NULL;
74439beb93cSSam Leffler 	size_t len;
74539beb93cSSam Leffler 
74639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
74739beb93cSSam Leffler 		   " Phase 2", (unsigned long) wpabuf_len(in_data));
74839beb93cSSam Leffler 
74939beb93cSSam Leffler 	if (data->pending_phase2_req) {
75039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
75139beb93cSSam Leffler 			   "skip decryption and use old data");
75239beb93cSSam Leffler 		/* Clear TLS reassembly state. */
75339beb93cSSam Leffler 		eap_peer_tls_reset_input(&data->ssl);
75439beb93cSSam Leffler 		in_decrypted = data->pending_phase2_req;
75539beb93cSSam Leffler 		data->pending_phase2_req = NULL;
75639beb93cSSam Leffler 		skip_change = 1;
75739beb93cSSam Leffler 		goto continue_req;
75839beb93cSSam Leffler 	}
75939beb93cSSam Leffler 
76039beb93cSSam Leffler 	if (wpabuf_len(in_data) == 0 && sm->workaround &&
76139beb93cSSam Leffler 	    data->phase2_success) {
76239beb93cSSam Leffler 		/*
76339beb93cSSam Leffler 		 * Cisco ACS seems to be using TLS ACK to terminate
76439beb93cSSam Leffler 		 * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
76539beb93cSSam Leffler 		 */
76639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
76739beb93cSSam Leffler 			   "expected data - acknowledge with TLS ACK since "
76839beb93cSSam Leffler 			   "Phase 2 has been completed");
76939beb93cSSam Leffler 		ret->decision = DECISION_COND_SUCC;
77039beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
77139beb93cSSam Leffler 		return 1;
77239beb93cSSam Leffler 	} else if (wpabuf_len(in_data) == 0) {
77339beb93cSSam Leffler 		/* Received TLS ACK - requesting more fragments */
77439beb93cSSam Leffler 		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
77539beb93cSSam Leffler 					    data->peap_version,
77639beb93cSSam Leffler 					    req->identifier, NULL, out_data);
77739beb93cSSam Leffler 	}
77839beb93cSSam Leffler 
77939beb93cSSam Leffler 	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
78039beb93cSSam Leffler 	if (res)
78139beb93cSSam Leffler 		return res;
78239beb93cSSam Leffler 
78339beb93cSSam Leffler continue_req:
78439beb93cSSam Leffler 	wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
78539beb93cSSam Leffler 			in_decrypted);
78639beb93cSSam Leffler 
78739beb93cSSam Leffler 	hdr = wpabuf_mhead(in_decrypted);
78839beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
78939beb93cSSam Leffler 	    be_to_host16(hdr->length) == 5 &&
79039beb93cSSam Leffler 	    eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
79139beb93cSSam Leffler 		/* At least FreeRADIUS seems to send full EAP header with
79239beb93cSSam Leffler 		 * EAP Request Identity */
79339beb93cSSam Leffler 		skip_change = 1;
79439beb93cSSam Leffler 	}
79539beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
79639beb93cSSam Leffler 	    eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
79739beb93cSSam Leffler 		skip_change = 1;
79839beb93cSSam Leffler 	}
79939beb93cSSam Leffler 
80039beb93cSSam Leffler 	if (data->peap_version == 0 && !skip_change) {
80139beb93cSSam Leffler 		struct eap_hdr *nhdr;
80239beb93cSSam Leffler 		struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
80339beb93cSSam Leffler 						   wpabuf_len(in_decrypted));
80439beb93cSSam Leffler 		if (nmsg == NULL) {
80539beb93cSSam Leffler 			wpabuf_free(in_decrypted);
80639beb93cSSam Leffler 			return 0;
80739beb93cSSam Leffler 		}
80839beb93cSSam Leffler 		nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
80939beb93cSSam Leffler 		wpabuf_put_buf(nmsg, in_decrypted);
81039beb93cSSam Leffler 		nhdr->code = req->code;
81139beb93cSSam Leffler 		nhdr->identifier = req->identifier;
81239beb93cSSam Leffler 		nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
81339beb93cSSam Leffler 					    wpabuf_len(in_decrypted));
81439beb93cSSam Leffler 
81539beb93cSSam Leffler 		wpabuf_free(in_decrypted);
81639beb93cSSam Leffler 		in_decrypted = nmsg;
81739beb93cSSam Leffler 	}
81839beb93cSSam Leffler 
81939beb93cSSam Leffler 	hdr = wpabuf_mhead(in_decrypted);
82039beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
82139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
82239beb93cSSam Leffler 			   "EAP frame (len=%lu)",
82339beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted));
82439beb93cSSam Leffler 		wpabuf_free(in_decrypted);
82539beb93cSSam Leffler 		return 0;
82639beb93cSSam Leffler 	}
82739beb93cSSam Leffler 	len = be_to_host16(hdr->length);
82839beb93cSSam Leffler 	if (len > wpabuf_len(in_decrypted)) {
82939beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
83039beb93cSSam Leffler 			   "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
83139beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted),
83239beb93cSSam Leffler 			   (unsigned long) len);
83339beb93cSSam Leffler 		wpabuf_free(in_decrypted);
83439beb93cSSam Leffler 		return 0;
83539beb93cSSam Leffler 	}
83639beb93cSSam Leffler 	if (len < wpabuf_len(in_decrypted)) {
83739beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
83839beb93cSSam Leffler 			   "shorter length than full decrypted data "
83939beb93cSSam Leffler 			   "(%lu < %lu)",
84039beb93cSSam Leffler 			   (unsigned long) len,
84139beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted));
84239beb93cSSam Leffler 	}
84339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
84439beb93cSSam Leffler 		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
84539beb93cSSam Leffler 		   (unsigned long) len);
84639beb93cSSam Leffler 	switch (hdr->code) {
84739beb93cSSam Leffler 	case EAP_CODE_REQUEST:
84839beb93cSSam Leffler 		if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
84939beb93cSSam Leffler 					    &resp)) {
85039beb93cSSam Leffler 			wpabuf_free(in_decrypted);
85139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
85239beb93cSSam Leffler 				   "processing failed");
85339beb93cSSam Leffler 			return 0;
85439beb93cSSam Leffler 		}
85539beb93cSSam Leffler 		break;
85639beb93cSSam Leffler 	case EAP_CODE_SUCCESS:
85739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
85839beb93cSSam Leffler 		if (data->peap_version == 1) {
85939beb93cSSam Leffler 			/* EAP-Success within TLS tunnel is used to indicate
86039beb93cSSam Leffler 			 * shutdown of the TLS channel. The authentication has
86139beb93cSSam Leffler 			 * been completed. */
86239beb93cSSam Leffler 			if (data->phase2_eap_started &&
86339beb93cSSam Leffler 			    !data->phase2_eap_success) {
86439beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
86539beb93cSSam Leffler 					   "Success used to indicate success, "
86639beb93cSSam Leffler 					   "but Phase 2 EAP was not yet "
86739beb93cSSam Leffler 					   "completed successfully");
86839beb93cSSam Leffler 				ret->methodState = METHOD_DONE;
86939beb93cSSam Leffler 				ret->decision = DECISION_FAIL;
87039beb93cSSam Leffler 				wpabuf_free(in_decrypted);
87139beb93cSSam Leffler 				return 0;
87239beb93cSSam Leffler 			}
87339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
87439beb93cSSam Leffler 				   "EAP-Success within TLS tunnel - "
87539beb93cSSam Leffler 				   "authentication completed");
87639beb93cSSam Leffler 			ret->decision = DECISION_UNCOND_SUCC;
87739beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
87839beb93cSSam Leffler 			data->phase2_success = 1;
87939beb93cSSam Leffler 			if (data->peap_outer_success == 2) {
88039beb93cSSam Leffler 				wpabuf_free(in_decrypted);
88139beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
88239beb93cSSam Leffler 					   "to finish authentication");
88339beb93cSSam Leffler 				return 1;
88439beb93cSSam Leffler 			} else if (data->peap_outer_success == 1) {
88539beb93cSSam Leffler 				/* Reply with EAP-Success within the TLS
88639beb93cSSam Leffler 				 * channel to complete the authentication. */
88739beb93cSSam Leffler 				resp = wpabuf_alloc(sizeof(struct eap_hdr));
88839beb93cSSam Leffler 				if (resp) {
88939beb93cSSam Leffler 					rhdr = wpabuf_put(resp, sizeof(*rhdr));
89039beb93cSSam Leffler 					rhdr->code = EAP_CODE_SUCCESS;
89139beb93cSSam Leffler 					rhdr->identifier = hdr->identifier;
89239beb93cSSam Leffler 					rhdr->length =
89339beb93cSSam Leffler 						host_to_be16(sizeof(*rhdr));
89439beb93cSSam Leffler 				}
89539beb93cSSam Leffler 			} else {
89639beb93cSSam Leffler 				/* No EAP-Success expected for Phase 1 (outer,
89739beb93cSSam Leffler 				 * unencrypted auth), so force EAP state
89839beb93cSSam Leffler 				 * machine to SUCCESS state. */
89939beb93cSSam Leffler 				sm->peap_done = TRUE;
90039beb93cSSam Leffler 			}
90139beb93cSSam Leffler 		} else {
90239beb93cSSam Leffler 			/* FIX: ? */
90339beb93cSSam Leffler 		}
90439beb93cSSam Leffler 		break;
90539beb93cSSam Leffler 	case EAP_CODE_FAILURE:
90639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
90739beb93cSSam Leffler 		ret->decision = DECISION_FAIL;
90839beb93cSSam Leffler 		ret->methodState = METHOD_MAY_CONT;
90939beb93cSSam Leffler 		ret->allowNotifications = FALSE;
91039beb93cSSam Leffler 		/* Reply with EAP-Failure within the TLS channel to complete
91139beb93cSSam Leffler 		 * failure reporting. */
91239beb93cSSam Leffler 		resp = wpabuf_alloc(sizeof(struct eap_hdr));
91339beb93cSSam Leffler 		if (resp) {
91439beb93cSSam Leffler 			rhdr = wpabuf_put(resp, sizeof(*rhdr));
91539beb93cSSam Leffler 			rhdr->code = EAP_CODE_FAILURE;
91639beb93cSSam Leffler 			rhdr->identifier = hdr->identifier;
91739beb93cSSam Leffler 			rhdr->length = host_to_be16(sizeof(*rhdr));
91839beb93cSSam Leffler 		}
91939beb93cSSam Leffler 		break;
92039beb93cSSam Leffler 	default:
92139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
92239beb93cSSam Leffler 			   "Phase 2 EAP header", hdr->code);
92339beb93cSSam Leffler 		break;
92439beb93cSSam Leffler 	}
92539beb93cSSam Leffler 
92639beb93cSSam Leffler 	wpabuf_free(in_decrypted);
92739beb93cSSam Leffler 
92839beb93cSSam Leffler 	if (resp) {
92939beb93cSSam Leffler 		int skip_change2 = 0;
93039beb93cSSam Leffler 		struct wpabuf *rmsg, buf;
93139beb93cSSam Leffler 
93239beb93cSSam Leffler 		wpa_hexdump_buf_key(MSG_DEBUG,
93339beb93cSSam Leffler 				    "EAP-PEAP: Encrypting Phase 2 data", resp);
93439beb93cSSam Leffler 		/* PEAP version changes */
93539beb93cSSam Leffler 		if (wpabuf_len(resp) >= 5 &&
93639beb93cSSam Leffler 		    wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
93739beb93cSSam Leffler 		    eap_get_type(resp) == EAP_TYPE_TLV)
93839beb93cSSam Leffler 			skip_change2 = 1;
93939beb93cSSam Leffler 		rmsg = resp;
94039beb93cSSam Leffler 		if (data->peap_version == 0 && !skip_change2) {
94139beb93cSSam Leffler 			wpabuf_set(&buf, wpabuf_head_u8(resp) +
94239beb93cSSam Leffler 				   sizeof(struct eap_hdr),
94339beb93cSSam Leffler 				   wpabuf_len(resp) - sizeof(struct eap_hdr));
94439beb93cSSam Leffler 			rmsg = &buf;
94539beb93cSSam Leffler 		}
94639beb93cSSam Leffler 
94739beb93cSSam Leffler 		if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
94839beb93cSSam Leffler 					 data->peap_version, req->identifier,
94939beb93cSSam Leffler 					 rmsg, out_data)) {
95039beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
95139beb93cSSam Leffler 				   "a Phase 2 frame");
95239beb93cSSam Leffler 		}
95339beb93cSSam Leffler 		wpabuf_free(resp);
95439beb93cSSam Leffler 	}
95539beb93cSSam Leffler 
95639beb93cSSam Leffler 	return 0;
95739beb93cSSam Leffler }
95839beb93cSSam Leffler 
95939beb93cSSam Leffler 
96039beb93cSSam Leffler static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
96139beb93cSSam Leffler 					struct eap_method_ret *ret,
96239beb93cSSam Leffler 					const struct wpabuf *reqData)
96339beb93cSSam Leffler {
96439beb93cSSam Leffler 	const struct eap_hdr *req;
96539beb93cSSam Leffler 	size_t left;
96639beb93cSSam Leffler 	int res;
96739beb93cSSam Leffler 	u8 flags, id;
96839beb93cSSam Leffler 	struct wpabuf *resp;
96939beb93cSSam Leffler 	const u8 *pos;
97039beb93cSSam Leffler 	struct eap_peap_data *data = priv;
971*325151a3SRui Paulo 	struct wpabuf msg;
97239beb93cSSam Leffler 
97339beb93cSSam Leffler 	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
97439beb93cSSam Leffler 					reqData, &left, &flags);
97539beb93cSSam Leffler 	if (pos == NULL)
97639beb93cSSam Leffler 		return NULL;
97739beb93cSSam Leffler 	req = wpabuf_head(reqData);
97839beb93cSSam Leffler 	id = req->identifier;
97939beb93cSSam Leffler 
98039beb93cSSam Leffler 	if (flags & EAP_TLS_FLAGS_START) {
98139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
982e28a4053SRui Paulo 			   "ver=%d)", flags & EAP_TLS_VERSION_MASK,
98339beb93cSSam Leffler 			data->peap_version);
984e28a4053SRui Paulo 		if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
985e28a4053SRui Paulo 			data->peap_version = flags & EAP_TLS_VERSION_MASK;
98639beb93cSSam Leffler 		if (data->force_peap_version >= 0 &&
98739beb93cSSam Leffler 		    data->force_peap_version != data->peap_version) {
98839beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
98939beb93cSSam Leffler 				   "forced PEAP version %d",
99039beb93cSSam Leffler 				   data->force_peap_version);
99139beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
99239beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
99339beb93cSSam Leffler 			ret->allowNotifications = FALSE;
99439beb93cSSam Leffler 			return NULL;
99539beb93cSSam Leffler 		}
99639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
99739beb93cSSam Leffler 			   data->peap_version);
99839beb93cSSam Leffler 		left = 0; /* make sure that this frame is empty, even though it
99939beb93cSSam Leffler 			   * should always be, anyway */
100039beb93cSSam Leffler 	}
100139beb93cSSam Leffler 
1002*325151a3SRui Paulo 	wpabuf_set(&msg, pos, left);
1003*325151a3SRui Paulo 
100439beb93cSSam Leffler 	resp = NULL;
100539beb93cSSam Leffler 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
100639beb93cSSam Leffler 	    !data->resuming) {
100739beb93cSSam Leffler 		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
100839beb93cSSam Leffler 	} else {
100939beb93cSSam Leffler 		res = eap_peer_tls_process_helper(sm, &data->ssl,
101039beb93cSSam Leffler 						  EAP_TYPE_PEAP,
1011*325151a3SRui Paulo 						  data->peap_version, id, &msg,
1012*325151a3SRui Paulo 						  &resp);
101339beb93cSSam Leffler 
1014*325151a3SRui Paulo 		if (res < 0) {
1015*325151a3SRui Paulo 			wpa_printf(MSG_DEBUG,
1016*325151a3SRui Paulo 				   "EAP-PEAP: TLS processing failed");
1017*325151a3SRui Paulo 			ret->methodState = METHOD_DONE;
1018*325151a3SRui Paulo 			ret->decision = DECISION_FAIL;
1019*325151a3SRui Paulo 			return resp;
1020*325151a3SRui Paulo 		}
102139beb93cSSam Leffler 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
102239beb93cSSam Leffler 			char *label;
102339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG,
102439beb93cSSam Leffler 				   "EAP-PEAP: TLS done, proceed to Phase 2");
10255b9c547cSRui Paulo 			eap_peap_free_key(data);
102639beb93cSSam Leffler 			/* draft-josefsson-ppext-eap-tls-eap-05.txt
102739beb93cSSam Leffler 			 * specifies that PEAPv1 would use "client PEAP
102839beb93cSSam Leffler 			 * encryption" as the label. However, most existing
102939beb93cSSam Leffler 			 * PEAPv1 implementations seem to be using the old
103039beb93cSSam Leffler 			 * label, "client EAP encryption", instead. Use the old
103139beb93cSSam Leffler 			 * label by default, but allow it to be configured with
103239beb93cSSam Leffler 			 * phase1 parameter peaplabel=1. */
10335b9c547cSRui Paulo 			if (data->force_new_label)
103439beb93cSSam Leffler 				label = "client PEAP encryption";
103539beb93cSSam Leffler 			else
103639beb93cSSam Leffler 				label = "client EAP encryption";
103739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
103839beb93cSSam Leffler 				   "key derivation", label);
103939beb93cSSam Leffler 			data->key_data =
104039beb93cSSam Leffler 				eap_peer_tls_derive_key(sm, &data->ssl, label,
104139beb93cSSam Leffler 							EAP_TLS_KEY_LEN);
104239beb93cSSam Leffler 			if (data->key_data) {
104339beb93cSSam Leffler 				wpa_hexdump_key(MSG_DEBUG,
104439beb93cSSam Leffler 						"EAP-PEAP: Derived key",
104539beb93cSSam Leffler 						data->key_data,
104639beb93cSSam Leffler 						EAP_TLS_KEY_LEN);
104739beb93cSSam Leffler 			} else {
104839beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
104939beb93cSSam Leffler 					   "derive key");
105039beb93cSSam Leffler 			}
105139beb93cSSam Leffler 
10525b9c547cSRui Paulo 			os_free(data->session_id);
10535b9c547cSRui Paulo 			data->session_id =
10545b9c547cSRui Paulo 				eap_peer_tls_derive_session_id(sm, &data->ssl,
10555b9c547cSRui Paulo 							       EAP_TYPE_PEAP,
10565b9c547cSRui Paulo 							       &data->id_len);
10575b9c547cSRui Paulo 			if (data->session_id) {
10585b9c547cSRui Paulo 				wpa_hexdump(MSG_DEBUG,
10595b9c547cSRui Paulo 					    "EAP-PEAP: Derived Session-Id",
10605b9c547cSRui Paulo 					    data->session_id, data->id_len);
10615b9c547cSRui Paulo 			} else {
10625b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
10635b9c547cSRui Paulo 					   "derive Session-Id");
10645b9c547cSRui Paulo 			}
10655b9c547cSRui Paulo 
106639beb93cSSam Leffler 			if (sm->workaround && data->resuming) {
106739beb93cSSam Leffler 				/*
106839beb93cSSam Leffler 				 * At least few RADIUS servers (Aegis v1.1.6;
106939beb93cSSam Leffler 				 * but not v1.1.4; and Cisco ACS) seem to be
107039beb93cSSam Leffler 				 * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
107139beb93cSSam Leffler 				 * ACS) session resumption with outer
107239beb93cSSam Leffler 				 * EAP-Success. This does not seem to follow
107339beb93cSSam Leffler 				 * draft-josefsson-pppext-eap-tls-eap-05.txt
107439beb93cSSam Leffler 				 * section 4.2, so only allow this if EAP
107539beb93cSSam Leffler 				 * workarounds are enabled.
107639beb93cSSam Leffler 				 */
107739beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
107839beb93cSSam Leffler 					   "allow outer EAP-Success to "
107939beb93cSSam Leffler 					   "terminate PEAP resumption");
108039beb93cSSam Leffler 				ret->decision = DECISION_COND_SUCC;
108139beb93cSSam Leffler 				data->phase2_success = 1;
108239beb93cSSam Leffler 			}
108339beb93cSSam Leffler 
108439beb93cSSam Leffler 			data->resuming = 0;
108539beb93cSSam Leffler 		}
108639beb93cSSam Leffler 
108739beb93cSSam Leffler 		if (res == 2) {
108839beb93cSSam Leffler 			/*
108939beb93cSSam Leffler 			 * Application data included in the handshake message.
109039beb93cSSam Leffler 			 */
109139beb93cSSam Leffler 			wpabuf_free(data->pending_phase2_req);
109239beb93cSSam Leffler 			data->pending_phase2_req = resp;
109339beb93cSSam Leffler 			resp = NULL;
109439beb93cSSam Leffler 			res = eap_peap_decrypt(sm, data, ret, req, &msg,
109539beb93cSSam Leffler 					       &resp);
109639beb93cSSam Leffler 		}
109739beb93cSSam Leffler 	}
109839beb93cSSam Leffler 
109939beb93cSSam Leffler 	if (ret->methodState == METHOD_DONE) {
110039beb93cSSam Leffler 		ret->allowNotifications = FALSE;
110139beb93cSSam Leffler 	}
110239beb93cSSam Leffler 
110339beb93cSSam Leffler 	if (res == 1) {
110439beb93cSSam Leffler 		wpabuf_free(resp);
110539beb93cSSam Leffler 		return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
110639beb93cSSam Leffler 					      data->peap_version);
110739beb93cSSam Leffler 	}
110839beb93cSSam Leffler 
110939beb93cSSam Leffler 	return resp;
111039beb93cSSam Leffler }
111139beb93cSSam Leffler 
111239beb93cSSam Leffler 
111339beb93cSSam Leffler static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
111439beb93cSSam Leffler {
111539beb93cSSam Leffler 	struct eap_peap_data *data = priv;
111639beb93cSSam Leffler 	return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
111739beb93cSSam Leffler 		data->phase2_success;
111839beb93cSSam Leffler }
111939beb93cSSam Leffler 
112039beb93cSSam Leffler 
112139beb93cSSam Leffler static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
112239beb93cSSam Leffler {
112339beb93cSSam Leffler 	struct eap_peap_data *data = priv;
112439beb93cSSam Leffler 	wpabuf_free(data->pending_phase2_req);
112539beb93cSSam Leffler 	data->pending_phase2_req = NULL;
112639beb93cSSam Leffler 	data->crypto_binding_used = 0;
112739beb93cSSam Leffler }
112839beb93cSSam Leffler 
112939beb93cSSam Leffler 
113039beb93cSSam Leffler static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
113139beb93cSSam Leffler {
113239beb93cSSam Leffler 	struct eap_peap_data *data = priv;
11335b9c547cSRui Paulo 	eap_peap_free_key(data);
11345b9c547cSRui Paulo 	os_free(data->session_id);
11355b9c547cSRui Paulo 	data->session_id = NULL;
113639beb93cSSam Leffler 	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
113739beb93cSSam Leffler 		os_free(data);
113839beb93cSSam Leffler 		return NULL;
113939beb93cSSam Leffler 	}
114039beb93cSSam Leffler 	if (data->phase2_priv && data->phase2_method &&
114139beb93cSSam Leffler 	    data->phase2_method->init_for_reauth)
114239beb93cSSam Leffler 		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
114339beb93cSSam Leffler 	data->phase2_success = 0;
114439beb93cSSam Leffler 	data->phase2_eap_success = 0;
114539beb93cSSam Leffler 	data->phase2_eap_started = 0;
114639beb93cSSam Leffler 	data->resuming = 1;
114739beb93cSSam Leffler 	data->reauth = 1;
114839beb93cSSam Leffler 	sm->peap_done = FALSE;
114939beb93cSSam Leffler 	return priv;
115039beb93cSSam Leffler }
115139beb93cSSam Leffler 
115239beb93cSSam Leffler 
115339beb93cSSam Leffler static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
115439beb93cSSam Leffler 			       size_t buflen, int verbose)
115539beb93cSSam Leffler {
115639beb93cSSam Leffler 	struct eap_peap_data *data = priv;
115739beb93cSSam Leffler 	int len, ret;
115839beb93cSSam Leffler 
115939beb93cSSam Leffler 	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
116039beb93cSSam Leffler 	if (data->phase2_method) {
116139beb93cSSam Leffler 		ret = os_snprintf(buf + len, buflen - len,
116239beb93cSSam Leffler 				  "EAP-PEAPv%d Phase2 method=%s\n",
116339beb93cSSam Leffler 				  data->peap_version,
116439beb93cSSam Leffler 				  data->phase2_method->name);
11655b9c547cSRui Paulo 		if (os_snprintf_error(buflen - len, ret))
116639beb93cSSam Leffler 			return len;
116739beb93cSSam Leffler 		len += ret;
116839beb93cSSam Leffler 	}
116939beb93cSSam Leffler 	return len;
117039beb93cSSam Leffler }
117139beb93cSSam Leffler 
117239beb93cSSam Leffler 
117339beb93cSSam Leffler static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
117439beb93cSSam Leffler {
117539beb93cSSam Leffler 	struct eap_peap_data *data = priv;
117639beb93cSSam Leffler 	return data->key_data != NULL && data->phase2_success;
117739beb93cSSam Leffler }
117839beb93cSSam Leffler 
117939beb93cSSam Leffler 
118039beb93cSSam Leffler static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
118139beb93cSSam Leffler {
118239beb93cSSam Leffler 	struct eap_peap_data *data = priv;
118339beb93cSSam Leffler 	u8 *key;
118439beb93cSSam Leffler 
118539beb93cSSam Leffler 	if (data->key_data == NULL || !data->phase2_success)
118639beb93cSSam Leffler 		return NULL;
118739beb93cSSam Leffler 
118839beb93cSSam Leffler 	key = os_malloc(EAP_TLS_KEY_LEN);
118939beb93cSSam Leffler 	if (key == NULL)
119039beb93cSSam Leffler 		return NULL;
119139beb93cSSam Leffler 
119239beb93cSSam Leffler 	*len = EAP_TLS_KEY_LEN;
119339beb93cSSam Leffler 
119439beb93cSSam Leffler 	if (data->crypto_binding_used) {
119539beb93cSSam Leffler 		u8 csk[128];
119639beb93cSSam Leffler 		/*
119739beb93cSSam Leffler 		 * Note: It looks like Microsoft implementation requires null
119839beb93cSSam Leffler 		 * termination for this label while the one used for deriving
119939beb93cSSam Leffler 		 * IPMK|CMK did not use null termination.
120039beb93cSSam Leffler 		 */
1201f05cddf9SRui Paulo 		if (peap_prfplus(data->peap_version, data->ipmk, 40,
120239beb93cSSam Leffler 				 "Session Key Generating Function",
1203f05cddf9SRui Paulo 				 (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
1204f05cddf9SRui Paulo 			os_free(key);
1205f05cddf9SRui Paulo 			return NULL;
1206f05cddf9SRui Paulo 		}
120739beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
120839beb93cSSam Leffler 		os_memcpy(key, csk, EAP_TLS_KEY_LEN);
120939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
121039beb93cSSam Leffler 			    key, EAP_TLS_KEY_LEN);
121139beb93cSSam Leffler 	} else
121239beb93cSSam Leffler 		os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
121339beb93cSSam Leffler 
121439beb93cSSam Leffler 	return key;
121539beb93cSSam Leffler }
121639beb93cSSam Leffler 
121739beb93cSSam Leffler 
12185b9c547cSRui Paulo static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
12195b9c547cSRui Paulo {
12205b9c547cSRui Paulo 	struct eap_peap_data *data = priv;
12215b9c547cSRui Paulo 	u8 *id;
12225b9c547cSRui Paulo 
12235b9c547cSRui Paulo 	if (data->session_id == NULL || !data->phase2_success)
12245b9c547cSRui Paulo 		return NULL;
12255b9c547cSRui Paulo 
12265b9c547cSRui Paulo 	id = os_malloc(data->id_len);
12275b9c547cSRui Paulo 	if (id == NULL)
12285b9c547cSRui Paulo 		return NULL;
12295b9c547cSRui Paulo 
12305b9c547cSRui Paulo 	*len = data->id_len;
12315b9c547cSRui Paulo 	os_memcpy(id, data->session_id, data->id_len);
12325b9c547cSRui Paulo 
12335b9c547cSRui Paulo 	return id;
12345b9c547cSRui Paulo }
12355b9c547cSRui Paulo 
12365b9c547cSRui Paulo 
123739beb93cSSam Leffler int eap_peer_peap_register(void)
123839beb93cSSam Leffler {
123939beb93cSSam Leffler 	struct eap_method *eap;
124039beb93cSSam Leffler 	int ret;
124139beb93cSSam Leffler 
124239beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
124339beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
124439beb93cSSam Leffler 	if (eap == NULL)
124539beb93cSSam Leffler 		return -1;
124639beb93cSSam Leffler 
124739beb93cSSam Leffler 	eap->init = eap_peap_init;
124839beb93cSSam Leffler 	eap->deinit = eap_peap_deinit;
124939beb93cSSam Leffler 	eap->process = eap_peap_process;
125039beb93cSSam Leffler 	eap->isKeyAvailable = eap_peap_isKeyAvailable;
125139beb93cSSam Leffler 	eap->getKey = eap_peap_getKey;
125239beb93cSSam Leffler 	eap->get_status = eap_peap_get_status;
125339beb93cSSam Leffler 	eap->has_reauth_data = eap_peap_has_reauth_data;
125439beb93cSSam Leffler 	eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
125539beb93cSSam Leffler 	eap->init_for_reauth = eap_peap_init_for_reauth;
12565b9c547cSRui Paulo 	eap->getSessionId = eap_peap_get_session_id;
125739beb93cSSam Leffler 
125839beb93cSSam Leffler 	ret = eap_peer_method_register(eap);
125939beb93cSSam Leffler 	if (ret)
126039beb93cSSam Leffler 		eap_peer_method_free(eap);
126139beb93cSSam Leffler 	return ret;
126239beb93cSSam Leffler }
1263