xref: /freebsd-src/contrib/wpa/src/eap_peer/eap_peap.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
34bc52338SCy Schubert  * Copyright (c) 2004-2019, 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;
62780fb4a2SCy Schubert 	struct wpabuf *pending_resp;
6339beb93cSSam Leffler 	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
6439beb93cSSam Leffler 	int crypto_binding_used;
6539beb93cSSam Leffler 	u8 binding_nonce[32];
6639beb93cSSam Leffler 	u8 ipmk[40];
6739beb93cSSam Leffler 	u8 cmk[20];
6839beb93cSSam Leffler 	int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
6939beb93cSSam Leffler 		  * is enabled. */
70*a90b9d01SCy Schubert 	enum { NO_AUTH, FOR_INITIAL, ALWAYS } phase2_auth;
7139beb93cSSam Leffler };
7239beb93cSSam Leffler 
7339beb93cSSam Leffler 
74780fb4a2SCy Schubert static void eap_peap_parse_phase1(struct eap_peap_data *data,
7539beb93cSSam Leffler 				  const char *phase1)
7639beb93cSSam Leffler {
7739beb93cSSam Leffler 	const char *pos;
7839beb93cSSam Leffler 
7939beb93cSSam Leffler 	pos = os_strstr(phase1, "peapver=");
8039beb93cSSam Leffler 	if (pos) {
8139beb93cSSam Leffler 		data->force_peap_version = atoi(pos + 8);
8239beb93cSSam Leffler 		data->peap_version = data->force_peap_version;
8339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
8439beb93cSSam Leffler 			   data->force_peap_version);
8539beb93cSSam Leffler 	}
8639beb93cSSam Leffler 
8739beb93cSSam Leffler 	if (os_strstr(phase1, "peaplabel=1")) {
8839beb93cSSam Leffler 		data->force_new_label = 1;
8939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
9039beb93cSSam Leffler 			   "derivation");
9139beb93cSSam Leffler 	}
9239beb93cSSam Leffler 
9339beb93cSSam Leffler 	if (os_strstr(phase1, "peap_outer_success=0")) {
9439beb93cSSam Leffler 		data->peap_outer_success = 0;
9539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
9639beb93cSSam Leffler 			   "tunneled EAP-Success");
9739beb93cSSam Leffler 	} else if (os_strstr(phase1, "peap_outer_success=1")) {
9839beb93cSSam Leffler 		data->peap_outer_success = 1;
9939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
10039beb93cSSam Leffler 			   "after receiving tunneled EAP-Success");
10139beb93cSSam Leffler 	} else if (os_strstr(phase1, "peap_outer_success=2")) {
10239beb93cSSam Leffler 		data->peap_outer_success = 2;
10339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
10439beb93cSSam Leffler 			   "receiving tunneled EAP-Success");
10539beb93cSSam Leffler 	}
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 	if (os_strstr(phase1, "crypto_binding=0")) {
10839beb93cSSam Leffler 		data->crypto_binding = NO_BINDING;
10939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
11039beb93cSSam Leffler 	} else if (os_strstr(phase1, "crypto_binding=1")) {
11139beb93cSSam Leffler 		data->crypto_binding = OPTIONAL_BINDING;
11239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
11339beb93cSSam Leffler 	} else if (os_strstr(phase1, "crypto_binding=2")) {
11439beb93cSSam Leffler 		data->crypto_binding = REQUIRE_BINDING;
11539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
11639beb93cSSam Leffler 	}
11739beb93cSSam Leffler 
118*a90b9d01SCy Schubert 	if (os_strstr(phase1, "phase2_auth=0")) {
119*a90b9d01SCy Schubert 		data->phase2_auth = NO_AUTH;
120*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
121*a90b9d01SCy Schubert 			   "EAP-PEAP: Do not require Phase 2 authentication");
122*a90b9d01SCy Schubert 	} else if (os_strstr(phase1, "phase2_auth=1")) {
123*a90b9d01SCy Schubert 		data->phase2_auth = FOR_INITIAL;
124*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
125*a90b9d01SCy Schubert 			   "EAP-PEAP: Require Phase 2 authentication for initial connection");
126*a90b9d01SCy Schubert 	} else if (os_strstr(phase1, "phase2_auth=2")) {
127*a90b9d01SCy Schubert 		data->phase2_auth = ALWAYS;
128*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
129*a90b9d01SCy Schubert 			   "EAP-PEAP: Require Phase 2 authentication for all cases");
130*a90b9d01SCy Schubert 	}
13139beb93cSSam Leffler #ifdef EAP_TNC
13239beb93cSSam Leffler 	if (os_strstr(phase1, "tnc=soh2")) {
13339beb93cSSam Leffler 		data->soh = 2;
13439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
13539beb93cSSam Leffler 	} else if (os_strstr(phase1, "tnc=soh1")) {
13639beb93cSSam Leffler 		data->soh = 1;
13739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
13839beb93cSSam Leffler 	} else if (os_strstr(phase1, "tnc=soh")) {
13939beb93cSSam Leffler 		data->soh = 2;
14039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
14139beb93cSSam Leffler 	}
14239beb93cSSam Leffler #endif /* EAP_TNC */
14339beb93cSSam Leffler }
14439beb93cSSam Leffler 
14539beb93cSSam Leffler 
14639beb93cSSam Leffler static void * eap_peap_init(struct eap_sm *sm)
14739beb93cSSam Leffler {
14839beb93cSSam Leffler 	struct eap_peap_data *data;
14939beb93cSSam Leffler 	struct eap_peer_config *config = eap_get_config(sm);
15039beb93cSSam Leffler 
15139beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
15239beb93cSSam Leffler 	if (data == NULL)
15339beb93cSSam Leffler 		return NULL;
154c1d255d3SCy Schubert 	sm->peap_done = false;
15539beb93cSSam Leffler 	data->peap_version = EAP_PEAP_VERSION;
15639beb93cSSam Leffler 	data->force_peap_version = -1;
15739beb93cSSam Leffler 	data->peap_outer_success = 2;
15839beb93cSSam Leffler 	data->crypto_binding = OPTIONAL_BINDING;
159*a90b9d01SCy Schubert 	data->phase2_auth = FOR_INITIAL;
16039beb93cSSam Leffler 
161780fb4a2SCy Schubert 	if (config && config->phase1)
162780fb4a2SCy Schubert 		eap_peap_parse_phase1(data, config->phase1);
16339beb93cSSam Leffler 
16439beb93cSSam Leffler 	if (eap_peer_select_phase2_methods(config, "auth=",
16539beb93cSSam Leffler 					   &data->phase2_types,
166c1d255d3SCy Schubert 					   &data->num_phase2_types, 0) < 0) {
16739beb93cSSam Leffler 		eap_peap_deinit(sm, data);
16839beb93cSSam Leffler 		return NULL;
16939beb93cSSam Leffler 	}
17039beb93cSSam Leffler 
17139beb93cSSam Leffler 	data->phase2_type.vendor = EAP_VENDOR_IETF;
17239beb93cSSam Leffler 	data->phase2_type.method = EAP_TYPE_NONE;
17339beb93cSSam Leffler 
174f05cddf9SRui Paulo 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
17539beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
17639beb93cSSam Leffler 		eap_peap_deinit(sm, data);
17739beb93cSSam Leffler 		return NULL;
17839beb93cSSam Leffler 	}
17939beb93cSSam Leffler 
18039beb93cSSam Leffler 	return data;
18139beb93cSSam Leffler }
18239beb93cSSam Leffler 
18339beb93cSSam Leffler 
1845b9c547cSRui Paulo static void eap_peap_free_key(struct eap_peap_data *data)
1855b9c547cSRui Paulo {
1865b9c547cSRui Paulo 	if (data->key_data) {
1874bc52338SCy Schubert 		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
1885b9c547cSRui Paulo 		data->key_data = NULL;
1895b9c547cSRui Paulo 	}
1905b9c547cSRui Paulo }
1915b9c547cSRui Paulo 
1925b9c547cSRui Paulo 
19339beb93cSSam Leffler static void eap_peap_deinit(struct eap_sm *sm, void *priv)
19439beb93cSSam Leffler {
19539beb93cSSam Leffler 	struct eap_peap_data *data = priv;
19639beb93cSSam Leffler 	if (data == NULL)
19739beb93cSSam Leffler 		return;
19839beb93cSSam Leffler 	if (data->phase2_priv && data->phase2_method)
19939beb93cSSam Leffler 		data->phase2_method->deinit(sm, data->phase2_priv);
20039beb93cSSam Leffler 	os_free(data->phase2_types);
20139beb93cSSam Leffler 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
2025b9c547cSRui Paulo 	eap_peap_free_key(data);
2035b9c547cSRui Paulo 	os_free(data->session_id);
2044bc52338SCy Schubert 	wpabuf_clear_free(data->pending_phase2_req);
2054bc52338SCy Schubert 	wpabuf_clear_free(data->pending_resp);
2064bc52338SCy Schubert 	bin_clear_free(data, sizeof(*data));
20739beb93cSSam Leffler }
20839beb93cSSam Leffler 
20939beb93cSSam Leffler 
21039beb93cSSam Leffler /**
21139beb93cSSam Leffler  * eap_tlv_build_nak - Build EAP-TLV NAK message
21239beb93cSSam Leffler  * @id: EAP identifier for the header
21339beb93cSSam Leffler  * @nak_type: TLV type (EAP_TLV_*)
21439beb93cSSam Leffler  * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
21539beb93cSSam Leffler  *
216f05cddf9SRui Paulo  * This function builds an EAP-TLV NAK message. The caller is responsible for
21739beb93cSSam Leffler  * freeing the returned buffer.
21839beb93cSSam Leffler  */
21939beb93cSSam Leffler static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
22039beb93cSSam Leffler {
22139beb93cSSam Leffler 	struct wpabuf *msg;
22239beb93cSSam Leffler 
22339beb93cSSam Leffler 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
22439beb93cSSam Leffler 			    EAP_CODE_RESPONSE, id);
22539beb93cSSam Leffler 	if (msg == NULL)
22639beb93cSSam Leffler 		return NULL;
22739beb93cSSam Leffler 
22839beb93cSSam Leffler 	wpabuf_put_u8(msg, 0x80); /* Mandatory */
22939beb93cSSam Leffler 	wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
23039beb93cSSam Leffler 	wpabuf_put_be16(msg, 6); /* Length */
23139beb93cSSam Leffler 	wpabuf_put_be32(msg, 0); /* Vendor-Id */
23239beb93cSSam Leffler 	wpabuf_put_be16(msg, nak_type); /* NAK-Type */
23339beb93cSSam Leffler 
23439beb93cSSam Leffler 	return msg;
23539beb93cSSam Leffler }
23639beb93cSSam Leffler 
23739beb93cSSam Leffler 
23839beb93cSSam Leffler static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
23939beb93cSSam Leffler 			    u8 *isk, size_t isk_len)
24039beb93cSSam Leffler {
24139beb93cSSam Leffler 	u8 *key;
24239beb93cSSam Leffler 	size_t key_len;
24339beb93cSSam Leffler 
24439beb93cSSam Leffler 	os_memset(isk, 0, isk_len);
24539beb93cSSam Leffler 	if (data->phase2_method == NULL || data->phase2_priv == NULL ||
24639beb93cSSam Leffler 	    data->phase2_method->isKeyAvailable == NULL ||
24739beb93cSSam Leffler 	    data->phase2_method->getKey == NULL)
24839beb93cSSam Leffler 		return 0;
24939beb93cSSam Leffler 
25039beb93cSSam Leffler 	if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
25139beb93cSSam Leffler 	    (key = data->phase2_method->getKey(sm, data->phase2_priv,
25239beb93cSSam Leffler 					       &key_len)) == NULL) {
25339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
25439beb93cSSam Leffler 			   "from Phase 2");
25539beb93cSSam Leffler 		return -1;
25639beb93cSSam Leffler 	}
25739beb93cSSam Leffler 
25839beb93cSSam Leffler 	if (key_len > isk_len)
25939beb93cSSam Leffler 		key_len = isk_len;
26039beb93cSSam Leffler 	os_memcpy(isk, key, key_len);
26139beb93cSSam Leffler 	os_free(key);
26239beb93cSSam Leffler 
26339beb93cSSam Leffler 	return 0;
26439beb93cSSam Leffler }
26539beb93cSSam Leffler 
26639beb93cSSam Leffler 
26739beb93cSSam Leffler static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
26839beb93cSSam Leffler {
26939beb93cSSam Leffler 	u8 *tk;
27039beb93cSSam Leffler 	u8 isk[32], imck[60];
2714bc52338SCy Schubert 	int resumed, res;
27239beb93cSSam Leffler 
27339beb93cSSam Leffler 	/*
27439beb93cSSam Leffler 	 * Tunnel key (TK) is the first 60 octets of the key generated by
27539beb93cSSam Leffler 	 * phase 1 of PEAP (based on TLS).
27639beb93cSSam Leffler 	 */
27739beb93cSSam Leffler 	tk = data->key_data;
27839beb93cSSam Leffler 	if (tk == NULL)
27939beb93cSSam Leffler 		return -1;
28039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
28139beb93cSSam Leffler 
282780fb4a2SCy Schubert 	resumed = tls_connection_resumed(sm->ssl_ctx, data->ssl.conn);
283780fb4a2SCy Schubert 	wpa_printf(MSG_DEBUG,
284780fb4a2SCy Schubert 		   "EAP-PEAP: CMK derivation - reauth=%d resumed=%d phase2_eap_started=%d phase2_success=%d",
285780fb4a2SCy Schubert 		   data->reauth, resumed, data->phase2_eap_started,
286780fb4a2SCy Schubert 		   data->phase2_success);
287780fb4a2SCy Schubert 	if (data->reauth && !data->phase2_eap_started && resumed) {
28839beb93cSSam Leffler 		/* Fast-connect: IPMK|CMK = TK */
28939beb93cSSam Leffler 		os_memcpy(data->ipmk, tk, 40);
29039beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
29139beb93cSSam Leffler 				data->ipmk, 40);
29239beb93cSSam Leffler 		os_memcpy(data->cmk, tk + 40, 20);
29339beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
29439beb93cSSam Leffler 				data->cmk, 20);
29539beb93cSSam Leffler 		return 0;
29639beb93cSSam Leffler 	}
29739beb93cSSam Leffler 
29839beb93cSSam Leffler 	if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
29939beb93cSSam Leffler 		return -1;
30039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
30139beb93cSSam Leffler 
30239beb93cSSam Leffler 	/*
30339beb93cSSam Leffler 	 * IPMK Seed = "Inner Methods Compound Keys" | ISK
30439beb93cSSam Leffler 	 * TempKey = First 40 octets of TK
30539beb93cSSam Leffler 	 * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
30639beb93cSSam Leffler 	 * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
30739beb93cSSam Leffler 	 * in the end of the label just before ISK; is that just a typo?)
30839beb93cSSam Leffler 	 */
30939beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
3104bc52338SCy Schubert 	res = peap_prfplus(data->peap_version, tk, 40,
311f05cddf9SRui Paulo 			   "Inner Methods Compound Keys",
3124bc52338SCy Schubert 			   isk, sizeof(isk), imck, sizeof(imck));
313206b73d0SCy Schubert 	forced_memzero(isk, sizeof(isk));
3144bc52338SCy Schubert 	if (res < 0)
315f05cddf9SRui Paulo 		return -1;
31639beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
31739beb93cSSam Leffler 			imck, sizeof(imck));
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 	os_memcpy(data->ipmk, imck, 40);
32039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
32139beb93cSSam Leffler 	os_memcpy(data->cmk, imck + 40, 20);
32239beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
323206b73d0SCy Schubert 	forced_memzero(imck, sizeof(imck));
32439beb93cSSam Leffler 
32539beb93cSSam Leffler 	return 0;
32639beb93cSSam Leffler }
32739beb93cSSam Leffler 
32839beb93cSSam Leffler 
32939beb93cSSam Leffler static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
33039beb93cSSam Leffler 				     struct eap_peap_data *data,
33139beb93cSSam Leffler 				     struct wpabuf *buf)
33239beb93cSSam Leffler {
33339beb93cSSam Leffler 	u8 *mac;
33439beb93cSSam Leffler 	u8 eap_type = EAP_TYPE_PEAP;
33539beb93cSSam Leffler 	const u8 *addr[2];
33639beb93cSSam Leffler 	size_t len[2];
33739beb93cSSam Leffler 	u16 tlv_type;
33839beb93cSSam Leffler 
33939beb93cSSam Leffler 	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
34039beb93cSSam Leffler 	addr[0] = wpabuf_put(buf, 0);
34139beb93cSSam Leffler 	len[0] = 60;
34239beb93cSSam Leffler 	addr[1] = &eap_type;
34339beb93cSSam Leffler 	len[1] = 1;
34439beb93cSSam Leffler 
34539beb93cSSam Leffler 	tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
34639beb93cSSam Leffler 	wpabuf_put_be16(buf, tlv_type);
34739beb93cSSam Leffler 	wpabuf_put_be16(buf, 56);
34839beb93cSSam Leffler 
34939beb93cSSam Leffler 	wpabuf_put_u8(buf, 0); /* Reserved */
35039beb93cSSam Leffler 	wpabuf_put_u8(buf, data->peap_version); /* Version */
35139beb93cSSam Leffler 	wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
35239beb93cSSam Leffler 	wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
35339beb93cSSam Leffler 	wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
35439beb93cSSam Leffler 	mac = wpabuf_put(buf, 20); /* Compound_MAC */
35539beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
35639beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
35739beb93cSSam Leffler 		    addr[0], len[0]);
35839beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
35939beb93cSSam Leffler 		    addr[1], len[1]);
360780fb4a2SCy Schubert 	if (hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac) < 0)
361780fb4a2SCy Schubert 		return -1;
36239beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
36339beb93cSSam Leffler 	data->crypto_binding_used = 1;
36439beb93cSSam Leffler 
36539beb93cSSam Leffler 	return 0;
36639beb93cSSam Leffler }
36739beb93cSSam Leffler 
36839beb93cSSam Leffler 
36939beb93cSSam Leffler /**
37039beb93cSSam Leffler  * eap_tlv_build_result - Build EAP-TLV Result message
37139beb93cSSam Leffler  * @id: EAP identifier for the header
37239beb93cSSam Leffler  * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
37339beb93cSSam Leffler  * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
37439beb93cSSam Leffler  *
375f05cddf9SRui Paulo  * This function builds an EAP-TLV Result message. The caller is responsible
376f05cddf9SRui Paulo  * for freeing the returned buffer.
37739beb93cSSam Leffler  */
37839beb93cSSam Leffler static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
37939beb93cSSam Leffler 					    struct eap_peap_data *data,
38039beb93cSSam Leffler 					    int crypto_tlv_used,
38139beb93cSSam Leffler 					    int id, u16 status)
38239beb93cSSam Leffler {
38339beb93cSSam Leffler 	struct wpabuf *msg;
38439beb93cSSam Leffler 	size_t len;
38539beb93cSSam Leffler 
38639beb93cSSam Leffler 	if (data->crypto_binding == NO_BINDING)
38739beb93cSSam Leffler 		crypto_tlv_used = 0;
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 	len = 6;
39039beb93cSSam Leffler 	if (crypto_tlv_used)
39139beb93cSSam Leffler 		len += 60; /* Cryptobinding TLV */
39239beb93cSSam Leffler 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
39339beb93cSSam Leffler 			    EAP_CODE_RESPONSE, id);
39439beb93cSSam Leffler 	if (msg == NULL)
39539beb93cSSam Leffler 		return NULL;
39639beb93cSSam Leffler 
39739beb93cSSam Leffler 	wpabuf_put_u8(msg, 0x80); /* Mandatory */
39839beb93cSSam Leffler 	wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
39939beb93cSSam Leffler 	wpabuf_put_be16(msg, 2); /* Length */
40039beb93cSSam Leffler 	wpabuf_put_be16(msg, status); /* Status */
40139beb93cSSam Leffler 
40239beb93cSSam Leffler 	if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
4034bc52338SCy Schubert 		wpabuf_clear_free(msg);
40439beb93cSSam Leffler 		return NULL;
40539beb93cSSam Leffler 	}
40639beb93cSSam Leffler 
40739beb93cSSam Leffler 	return msg;
40839beb93cSSam Leffler }
40939beb93cSSam Leffler 
41039beb93cSSam Leffler 
41139beb93cSSam Leffler static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
41239beb93cSSam Leffler 					  struct eap_peap_data *data,
41339beb93cSSam Leffler 					  const u8 *crypto_tlv,
41439beb93cSSam Leffler 					  size_t crypto_tlv_len)
41539beb93cSSam Leffler {
41639beb93cSSam Leffler 	u8 buf[61], mac[SHA1_MAC_LEN];
41739beb93cSSam Leffler 	const u8 *pos;
41839beb93cSSam Leffler 
41939beb93cSSam Leffler 	if (eap_peap_derive_cmk(sm, data) < 0) {
42039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
42139beb93cSSam Leffler 		return -1;
42239beb93cSSam Leffler 	}
42339beb93cSSam Leffler 
42439beb93cSSam Leffler 	if (crypto_tlv_len != 4 + 56) {
42539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
42639beb93cSSam Leffler 			   "length %d", (int) crypto_tlv_len);
42739beb93cSSam Leffler 		return -1;
42839beb93cSSam Leffler 	}
42939beb93cSSam Leffler 
43039beb93cSSam Leffler 	pos = crypto_tlv;
43139beb93cSSam Leffler 	pos += 4; /* TLV header */
43239beb93cSSam Leffler 	if (pos[1] != data->peap_version) {
43339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
43439beb93cSSam Leffler 			   "mismatch (was %d; expected %d)",
43539beb93cSSam Leffler 			   pos[1], data->peap_version);
43639beb93cSSam Leffler 		return -1;
43739beb93cSSam Leffler 	}
43839beb93cSSam Leffler 
43939beb93cSSam Leffler 	if (pos[3] != 0) {
44039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
44139beb93cSSam Leffler 			   "SubType %d", pos[3]);
44239beb93cSSam Leffler 		return -1;
44339beb93cSSam Leffler 	}
44439beb93cSSam Leffler 	pos += 4;
44539beb93cSSam Leffler 	os_memcpy(data->binding_nonce, pos, 32);
44639beb93cSSam Leffler 	pos += 32; /* Nonce */
44739beb93cSSam Leffler 
44839beb93cSSam Leffler 	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
44939beb93cSSam Leffler 	os_memcpy(buf, crypto_tlv, 60);
45039beb93cSSam Leffler 	os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
45139beb93cSSam Leffler 	buf[60] = EAP_TYPE_PEAP;
45239beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
45339beb93cSSam Leffler 		    buf, sizeof(buf));
45439beb93cSSam Leffler 	hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
45539beb93cSSam Leffler 
4565b9c547cSRui Paulo 	if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
45739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
45839beb93cSSam Leffler 			   "cryptobinding TLV");
45939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
46039beb93cSSam Leffler 			    pos, SHA1_MAC_LEN);
46139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
46239beb93cSSam Leffler 			    mac, SHA1_MAC_LEN);
46339beb93cSSam Leffler 		return -1;
46439beb93cSSam Leffler 	}
46539beb93cSSam Leffler 
46639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
46739beb93cSSam Leffler 
46839beb93cSSam Leffler 	return 0;
46939beb93cSSam Leffler }
47039beb93cSSam Leffler 
47139beb93cSSam Leffler 
472*a90b9d01SCy Schubert static bool peap_phase2_sufficient(struct eap_sm *sm,
473*a90b9d01SCy Schubert 				   struct eap_peap_data *data)
474*a90b9d01SCy Schubert {
475*a90b9d01SCy Schubert 	if ((data->phase2_auth == ALWAYS ||
476*a90b9d01SCy Schubert 	     (data->phase2_auth == FOR_INITIAL &&
477*a90b9d01SCy Schubert 	      !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn) &&
478*a90b9d01SCy Schubert 	      !data->ssl.client_cert_conf) ||
479*a90b9d01SCy Schubert 	     data->phase2_eap_started) &&
480*a90b9d01SCy Schubert 	    !data->phase2_eap_success)
481*a90b9d01SCy Schubert 		return false;
482*a90b9d01SCy Schubert 	return true;
483*a90b9d01SCy Schubert }
484*a90b9d01SCy Schubert 
485*a90b9d01SCy Schubert 
48639beb93cSSam Leffler /**
48739beb93cSSam Leffler  * eap_tlv_process - Process a received EAP-TLV message and generate a response
48839beb93cSSam Leffler  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
48939beb93cSSam Leffler  * @ret: Return values from EAP request validation and processing
49039beb93cSSam Leffler  * @req: EAP-TLV request to be processed. The caller must have validated that
49139beb93cSSam Leffler  * the buffer is large enough to contain full request (hdr->length bytes) and
49239beb93cSSam Leffler  * that the EAP type is EAP_TYPE_TLV.
49339beb93cSSam Leffler  * @resp: Buffer to return a pointer to the allocated response message. This
49439beb93cSSam Leffler  * field should be initialized to %NULL before the call. The value will be
49539beb93cSSam Leffler  * updated if a response message is generated. The caller is responsible for
49639beb93cSSam Leffler  * freeing the allocated message.
49739beb93cSSam Leffler  * @force_failure: Force negotiation to fail
49839beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
49939beb93cSSam Leffler  */
50039beb93cSSam Leffler static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
50139beb93cSSam Leffler 			   struct eap_method_ret *ret,
50239beb93cSSam Leffler 			   const struct wpabuf *req, struct wpabuf **resp,
50339beb93cSSam Leffler 			   int force_failure)
50439beb93cSSam Leffler {
50539beb93cSSam Leffler 	size_t left, tlv_len;
50639beb93cSSam Leffler 	const u8 *pos;
50739beb93cSSam Leffler 	const u8 *result_tlv = NULL, *crypto_tlv = NULL;
50839beb93cSSam Leffler 	size_t result_tlv_len = 0, crypto_tlv_len = 0;
50939beb93cSSam Leffler 	int tlv_type, mandatory;
51039beb93cSSam Leffler 
51139beb93cSSam Leffler 	/* Parse TLVs */
51239beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
51339beb93cSSam Leffler 	if (pos == NULL)
51439beb93cSSam Leffler 		return -1;
51539beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
51639beb93cSSam Leffler 	while (left >= 4) {
51739beb93cSSam Leffler 		mandatory = !!(pos[0] & 0x80);
51839beb93cSSam Leffler 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
51939beb93cSSam Leffler 		pos += 2;
52039beb93cSSam Leffler 		tlv_len = WPA_GET_BE16(pos);
52139beb93cSSam Leffler 		pos += 2;
52239beb93cSSam Leffler 		left -= 4;
52339beb93cSSam Leffler 		if (tlv_len > left) {
52439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
52539beb93cSSam Leffler 				   "(tlv_len=%lu left=%lu)",
52639beb93cSSam Leffler 				   (unsigned long) tlv_len,
52739beb93cSSam Leffler 				   (unsigned long) left);
52839beb93cSSam Leffler 			return -1;
52939beb93cSSam Leffler 		}
53039beb93cSSam Leffler 		switch (tlv_type) {
53139beb93cSSam Leffler 		case EAP_TLV_RESULT_TLV:
53239beb93cSSam Leffler 			result_tlv = pos;
53339beb93cSSam Leffler 			result_tlv_len = tlv_len;
53439beb93cSSam Leffler 			break;
53539beb93cSSam Leffler 		case EAP_TLV_CRYPTO_BINDING_TLV:
53639beb93cSSam Leffler 			crypto_tlv = pos;
53739beb93cSSam Leffler 			crypto_tlv_len = tlv_len;
53839beb93cSSam Leffler 			break;
53939beb93cSSam Leffler 		default:
54039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
54139beb93cSSam Leffler 				   "%d%s", tlv_type,
54239beb93cSSam Leffler 				   mandatory ? " (mandatory)" : "");
54339beb93cSSam Leffler 			if (mandatory) {
54439beb93cSSam Leffler 				/* NAK TLV and ignore all TLVs in this packet.
54539beb93cSSam Leffler 				 */
54639beb93cSSam Leffler 				*resp = eap_tlv_build_nak(eap_get_id(req),
54739beb93cSSam Leffler 							  tlv_type);
54839beb93cSSam Leffler 				return *resp == NULL ? -1 : 0;
54939beb93cSSam Leffler 			}
55039beb93cSSam Leffler 			/* Ignore this TLV, but process other TLVs */
55139beb93cSSam Leffler 			break;
55239beb93cSSam Leffler 		}
55339beb93cSSam Leffler 
55439beb93cSSam Leffler 		pos += tlv_len;
55539beb93cSSam Leffler 		left -= tlv_len;
55639beb93cSSam Leffler 	}
55739beb93cSSam Leffler 	if (left) {
55839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
55939beb93cSSam Leffler 			   "Request (left=%lu)", (unsigned long) left);
56039beb93cSSam Leffler 		return -1;
56139beb93cSSam Leffler 	}
56239beb93cSSam Leffler 
56339beb93cSSam Leffler 	/* Process supported TLVs */
56439beb93cSSam Leffler 	if (crypto_tlv && data->crypto_binding != NO_BINDING) {
56539beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
56639beb93cSSam Leffler 			    crypto_tlv, crypto_tlv_len);
56739beb93cSSam Leffler 		if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
56839beb93cSSam Leffler 						   crypto_tlv_len + 4) < 0) {
56939beb93cSSam Leffler 			if (result_tlv == NULL)
57039beb93cSSam Leffler 				return -1;
57139beb93cSSam Leffler 			force_failure = 1;
57239beb93cSSam Leffler 			crypto_tlv = NULL; /* do not include Cryptobinding TLV
57339beb93cSSam Leffler 					    * in response, if the received
57439beb93cSSam Leffler 					    * cryptobinding was invalid. */
57539beb93cSSam Leffler 		}
57639beb93cSSam Leffler 	} else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
57739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
57839beb93cSSam Leffler 		return -1;
57939beb93cSSam Leffler 	}
58039beb93cSSam Leffler 
58139beb93cSSam Leffler 	if (result_tlv) {
58239beb93cSSam Leffler 		int status, resp_status;
58339beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
58439beb93cSSam Leffler 			    result_tlv, result_tlv_len);
58539beb93cSSam Leffler 		if (result_tlv_len < 2) {
58639beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
58739beb93cSSam Leffler 				   "(len=%lu)",
58839beb93cSSam Leffler 				   (unsigned long) result_tlv_len);
58939beb93cSSam Leffler 			return -1;
59039beb93cSSam Leffler 		}
59139beb93cSSam Leffler 		status = WPA_GET_BE16(result_tlv);
59239beb93cSSam Leffler 		if (status == EAP_TLV_RESULT_SUCCESS) {
59339beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
59439beb93cSSam Leffler 				   "- EAP-TLV/Phase2 Completed");
59539beb93cSSam Leffler 			if (force_failure) {
59639beb93cSSam Leffler 				wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
59739beb93cSSam Leffler 					   " - force failed Phase 2");
59839beb93cSSam Leffler 				resp_status = EAP_TLV_RESULT_FAILURE;
59939beb93cSSam Leffler 				ret->decision = DECISION_FAIL;
600*a90b9d01SCy Schubert 			} else if (!peap_phase2_sufficient(sm, data)) {
601*a90b9d01SCy Schubert 				wpa_printf(MSG_INFO,
602*a90b9d01SCy Schubert 					   "EAP-PEAP: Server indicated Phase 2 success, but sufficient Phase 2 authentication has not been completed");
603*a90b9d01SCy Schubert 				resp_status = EAP_TLV_RESULT_FAILURE;
604*a90b9d01SCy Schubert 				ret->decision = DECISION_FAIL;
60539beb93cSSam Leffler 			} else {
60639beb93cSSam Leffler 				resp_status = EAP_TLV_RESULT_SUCCESS;
60739beb93cSSam Leffler 				ret->decision = DECISION_UNCOND_SUCC;
60839beb93cSSam Leffler 			}
60939beb93cSSam Leffler 		} else if (status == EAP_TLV_RESULT_FAILURE) {
61039beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
61139beb93cSSam Leffler 			resp_status = EAP_TLV_RESULT_FAILURE;
61239beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
61339beb93cSSam Leffler 		} else {
61439beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
61539beb93cSSam Leffler 				   "Status %d", status);
61639beb93cSSam Leffler 			resp_status = EAP_TLV_RESULT_FAILURE;
61739beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
61839beb93cSSam Leffler 		}
61939beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
62039beb93cSSam Leffler 
62139beb93cSSam Leffler 		*resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
62239beb93cSSam Leffler 					     eap_get_id(req), resp_status);
62339beb93cSSam Leffler 	}
62439beb93cSSam Leffler 
62539beb93cSSam Leffler 	return 0;
62639beb93cSSam Leffler }
62739beb93cSSam Leffler 
62839beb93cSSam Leffler 
62939beb93cSSam Leffler static int eap_peap_phase2_request(struct eap_sm *sm,
63039beb93cSSam Leffler 				   struct eap_peap_data *data,
63139beb93cSSam Leffler 				   struct eap_method_ret *ret,
63239beb93cSSam Leffler 				   struct wpabuf *req,
63339beb93cSSam Leffler 				   struct wpabuf **resp)
63439beb93cSSam Leffler {
63539beb93cSSam Leffler 	struct eap_hdr *hdr = wpabuf_mhead(req);
63639beb93cSSam Leffler 	size_t len = be_to_host16(hdr->length);
63739beb93cSSam Leffler 	u8 *pos;
63839beb93cSSam Leffler 	struct eap_method_ret iret;
63939beb93cSSam Leffler 	struct eap_peer_config *config = eap_get_config(sm);
640c1d255d3SCy Schubert 	int vendor;
641c1d255d3SCy Schubert 	enum eap_type method;
64239beb93cSSam Leffler 
64339beb93cSSam Leffler 	if (len <= sizeof(struct eap_hdr)) {
64439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: too short "
64539beb93cSSam Leffler 			   "Phase 2 request (len=%lu)", (unsigned long) len);
64639beb93cSSam Leffler 		return -1;
64739beb93cSSam Leffler 	}
64839beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
64939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
65039beb93cSSam Leffler 	switch (*pos) {
65139beb93cSSam Leffler 	case EAP_TYPE_IDENTITY:
65239beb93cSSam Leffler 		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
65339beb93cSSam Leffler 		break;
65439beb93cSSam Leffler 	case EAP_TYPE_TLV:
65539beb93cSSam Leffler 		os_memset(&iret, 0, sizeof(iret));
65639beb93cSSam Leffler 		if (eap_tlv_process(sm, data, &iret, req, resp,
65739beb93cSSam Leffler 				    data->phase2_eap_started &&
65839beb93cSSam Leffler 				    !data->phase2_eap_success)) {
65939beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
66039beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
66139beb93cSSam Leffler 			return -1;
66239beb93cSSam Leffler 		}
66339beb93cSSam Leffler 		if (iret.methodState == METHOD_DONE ||
66439beb93cSSam Leffler 		    iret.methodState == METHOD_MAY_CONT) {
66539beb93cSSam Leffler 			ret->methodState = iret.methodState;
66639beb93cSSam Leffler 			ret->decision = iret.decision;
66739beb93cSSam Leffler 			data->phase2_success = 1;
66839beb93cSSam Leffler 		}
66939beb93cSSam Leffler 		break;
67039beb93cSSam Leffler 	case EAP_TYPE_EXPANDED:
67139beb93cSSam Leffler #ifdef EAP_TNC
67239beb93cSSam Leffler 		if (data->soh) {
67339beb93cSSam Leffler 			const u8 *epos;
67439beb93cSSam Leffler 			size_t eleft;
67539beb93cSSam Leffler 
67639beb93cSSam Leffler 			epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
67739beb93cSSam Leffler 						req, &eleft);
67839beb93cSSam Leffler 			if (epos) {
67939beb93cSSam Leffler 				struct wpabuf *buf;
68039beb93cSSam Leffler 				wpa_printf(MSG_DEBUG,
68139beb93cSSam Leffler 					   "EAP-PEAP: SoH EAP Extensions");
68239beb93cSSam Leffler 				buf = tncc_process_soh_request(data->soh,
68339beb93cSSam Leffler 							       epos, eleft);
68439beb93cSSam Leffler 				if (buf) {
68539beb93cSSam Leffler 					*resp = eap_msg_alloc(
68639beb93cSSam Leffler 						EAP_VENDOR_MICROSOFT, 0x21,
68739beb93cSSam Leffler 						wpabuf_len(buf),
68839beb93cSSam Leffler 						EAP_CODE_RESPONSE,
68939beb93cSSam Leffler 						hdr->identifier);
69039beb93cSSam Leffler 					if (*resp == NULL) {
69139beb93cSSam Leffler 						ret->methodState = METHOD_DONE;
69239beb93cSSam Leffler 						ret->decision = DECISION_FAIL;
6934bc52338SCy Schubert 						wpabuf_clear_free(buf);
69439beb93cSSam Leffler 						return -1;
69539beb93cSSam Leffler 					}
69639beb93cSSam Leffler 					wpabuf_put_buf(*resp, buf);
6974bc52338SCy Schubert 					wpabuf_clear_free(buf);
69839beb93cSSam Leffler 					break;
69939beb93cSSam Leffler 				}
70039beb93cSSam Leffler 			}
70139beb93cSSam Leffler 		}
70239beb93cSSam Leffler #endif /* EAP_TNC */
70339beb93cSSam Leffler 		/* fall through */
70439beb93cSSam Leffler 	default:
705c1d255d3SCy Schubert 		vendor = EAP_VENDOR_IETF;
706c1d255d3SCy Schubert 		method = *pos;
707c1d255d3SCy Schubert 
708c1d255d3SCy Schubert 		if (method == EAP_TYPE_EXPANDED) {
709c1d255d3SCy Schubert 			if (len < sizeof(struct eap_hdr) + 8) {
710c1d255d3SCy Schubert 				wpa_printf(MSG_INFO,
711c1d255d3SCy Schubert 					   "EAP-PEAP: Too short Phase 2 request (expanded header) (len=%lu)",
712c1d255d3SCy Schubert 					   (unsigned long) len);
713c1d255d3SCy Schubert 				return -1;
714c1d255d3SCy Schubert 			}
715c1d255d3SCy Schubert 			vendor = WPA_GET_BE24(pos + 1);
716c1d255d3SCy Schubert 			method = WPA_GET_BE32(pos + 4);
717c1d255d3SCy Schubert 		}
718c1d255d3SCy Schubert 
71939beb93cSSam Leffler 		if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
72039beb93cSSam Leffler 		    data->phase2_type.method == EAP_TYPE_NONE) {
72139beb93cSSam Leffler 			size_t i;
72239beb93cSSam Leffler 			for (i = 0; i < data->num_phase2_types; i++) {
723c1d255d3SCy Schubert 				if (data->phase2_types[i].vendor != vendor ||
724c1d255d3SCy Schubert 				    data->phase2_types[i].method != method)
72539beb93cSSam Leffler 					continue;
72639beb93cSSam Leffler 
72739beb93cSSam Leffler 				data->phase2_type.vendor =
72839beb93cSSam Leffler 					data->phase2_types[i].vendor;
72939beb93cSSam Leffler 				data->phase2_type.method =
73039beb93cSSam Leffler 					data->phase2_types[i].method;
73139beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
73239beb93cSSam Leffler 					   "Phase 2 EAP vendor %d method %d",
73339beb93cSSam Leffler 					   data->phase2_type.vendor,
73439beb93cSSam Leffler 					   data->phase2_type.method);
73539beb93cSSam Leffler 				break;
73639beb93cSSam Leffler 			}
73739beb93cSSam Leffler 		}
738c1d255d3SCy Schubert 		if (vendor != data->phase2_type.vendor ||
739c1d255d3SCy Schubert 		    method != data->phase2_type.method ||
740c1d255d3SCy Schubert 		    (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE)) {
74139beb93cSSam Leffler 			if (eap_peer_tls_phase2_nak(data->phase2_types,
74239beb93cSSam Leffler 						    data->num_phase2_types,
74339beb93cSSam Leffler 						    hdr, resp))
74439beb93cSSam Leffler 				return -1;
74539beb93cSSam Leffler 			return 0;
74639beb93cSSam Leffler 		}
74739beb93cSSam Leffler 
74839beb93cSSam Leffler 		if (data->phase2_priv == NULL) {
74939beb93cSSam Leffler 			data->phase2_method = eap_peer_get_eap_method(
75039beb93cSSam Leffler 				data->phase2_type.vendor,
75139beb93cSSam Leffler 				data->phase2_type.method);
75239beb93cSSam Leffler 			if (data->phase2_method) {
75339beb93cSSam Leffler 				sm->init_phase2 = 1;
75439beb93cSSam Leffler 				data->phase2_priv =
75539beb93cSSam Leffler 					data->phase2_method->init(sm);
75639beb93cSSam Leffler 				sm->init_phase2 = 0;
75739beb93cSSam Leffler 			}
75839beb93cSSam Leffler 		}
75939beb93cSSam Leffler 		if (data->phase2_priv == NULL || data->phase2_method == NULL) {
76039beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
76139beb93cSSam Leffler 				   "Phase 2 EAP method %d", *pos);
76239beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
76339beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
76439beb93cSSam Leffler 			return -1;
76539beb93cSSam Leffler 		}
76639beb93cSSam Leffler 		data->phase2_eap_started = 1;
76739beb93cSSam Leffler 		os_memset(&iret, 0, sizeof(iret));
76839beb93cSSam Leffler 		*resp = data->phase2_method->process(sm, data->phase2_priv,
76939beb93cSSam Leffler 						     &iret, req);
77039beb93cSSam Leffler 		if ((iret.methodState == METHOD_DONE ||
77139beb93cSSam Leffler 		     iret.methodState == METHOD_MAY_CONT) &&
77239beb93cSSam Leffler 		    (iret.decision == DECISION_UNCOND_SUCC ||
77339beb93cSSam Leffler 		     iret.decision == DECISION_COND_SUCC)) {
77439beb93cSSam Leffler 			data->phase2_eap_success = 1;
77539beb93cSSam Leffler 			data->phase2_success = 1;
77639beb93cSSam Leffler 		}
77739beb93cSSam Leffler 		break;
77839beb93cSSam Leffler 	}
77939beb93cSSam Leffler 
78039beb93cSSam Leffler 	if (*resp == NULL &&
78139beb93cSSam Leffler 	    (config->pending_req_identity || config->pending_req_password ||
78285732ac8SCy Schubert 	     config->pending_req_otp || config->pending_req_new_password ||
78385732ac8SCy Schubert 	     config->pending_req_sim)) {
7844bc52338SCy Schubert 		wpabuf_clear_free(data->pending_phase2_req);
78539beb93cSSam Leffler 		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
78639beb93cSSam Leffler 	}
78739beb93cSSam Leffler 
78839beb93cSSam Leffler 	return 0;
78939beb93cSSam Leffler }
79039beb93cSSam Leffler 
79139beb93cSSam Leffler 
79239beb93cSSam Leffler static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
79339beb93cSSam Leffler 			    struct eap_method_ret *ret,
79439beb93cSSam Leffler 			    const struct eap_hdr *req,
79539beb93cSSam Leffler 			    const struct wpabuf *in_data,
79639beb93cSSam Leffler 			    struct wpabuf **out_data)
79739beb93cSSam Leffler {
79839beb93cSSam Leffler 	struct wpabuf *in_decrypted = NULL;
79939beb93cSSam Leffler 	int res, skip_change = 0;
80039beb93cSSam Leffler 	struct eap_hdr *hdr, *rhdr;
80139beb93cSSam Leffler 	struct wpabuf *resp = NULL;
80239beb93cSSam Leffler 	size_t len;
80339beb93cSSam Leffler 
80439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
80539beb93cSSam Leffler 		   " Phase 2", (unsigned long) wpabuf_len(in_data));
80639beb93cSSam Leffler 
80739beb93cSSam Leffler 	if (data->pending_phase2_req) {
80839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
80939beb93cSSam Leffler 			   "skip decryption and use old data");
81039beb93cSSam Leffler 		/* Clear TLS reassembly state. */
81139beb93cSSam Leffler 		eap_peer_tls_reset_input(&data->ssl);
81239beb93cSSam Leffler 		in_decrypted = data->pending_phase2_req;
81339beb93cSSam Leffler 		data->pending_phase2_req = NULL;
81439beb93cSSam Leffler 		skip_change = 1;
81539beb93cSSam Leffler 		goto continue_req;
81639beb93cSSam Leffler 	}
81739beb93cSSam Leffler 
81839beb93cSSam Leffler 	if (wpabuf_len(in_data) == 0 && sm->workaround &&
81939beb93cSSam Leffler 	    data->phase2_success) {
82039beb93cSSam Leffler 		/*
82139beb93cSSam Leffler 		 * Cisco ACS seems to be using TLS ACK to terminate
82239beb93cSSam Leffler 		 * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
82339beb93cSSam Leffler 		 */
82439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
82539beb93cSSam Leffler 			   "expected data - acknowledge with TLS ACK since "
82639beb93cSSam Leffler 			   "Phase 2 has been completed");
82739beb93cSSam Leffler 		ret->decision = DECISION_COND_SUCC;
82839beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
82939beb93cSSam Leffler 		return 1;
83039beb93cSSam Leffler 	} else if (wpabuf_len(in_data) == 0) {
83139beb93cSSam Leffler 		/* Received TLS ACK - requesting more fragments */
83239beb93cSSam Leffler 		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
83339beb93cSSam Leffler 					    data->peap_version,
83439beb93cSSam Leffler 					    req->identifier, NULL, out_data);
83539beb93cSSam Leffler 	}
83639beb93cSSam Leffler 
83739beb93cSSam Leffler 	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
83839beb93cSSam Leffler 	if (res)
83939beb93cSSam Leffler 		return res;
840c1d255d3SCy Schubert 	if (wpabuf_len(in_decrypted) == 0) {
841c1d255d3SCy Schubert 		wpabuf_free(in_decrypted);
842c1d255d3SCy Schubert 		return 1;
843c1d255d3SCy Schubert 	}
84439beb93cSSam Leffler 
84539beb93cSSam Leffler continue_req:
84639beb93cSSam Leffler 	wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
84739beb93cSSam Leffler 			in_decrypted);
84839beb93cSSam Leffler 
84939beb93cSSam Leffler 	hdr = wpabuf_mhead(in_decrypted);
85039beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
85139beb93cSSam Leffler 	    be_to_host16(hdr->length) == 5 &&
85239beb93cSSam Leffler 	    eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
85339beb93cSSam Leffler 		/* At least FreeRADIUS seems to send full EAP header with
85439beb93cSSam Leffler 		 * EAP Request Identity */
85539beb93cSSam Leffler 		skip_change = 1;
85639beb93cSSam Leffler 	}
85739beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
85839beb93cSSam Leffler 	    eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
85939beb93cSSam Leffler 		skip_change = 1;
86039beb93cSSam Leffler 	}
86139beb93cSSam Leffler 
86239beb93cSSam Leffler 	if (data->peap_version == 0 && !skip_change) {
86339beb93cSSam Leffler 		struct eap_hdr *nhdr;
86439beb93cSSam Leffler 		struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
86539beb93cSSam Leffler 						   wpabuf_len(in_decrypted));
86639beb93cSSam Leffler 		if (nmsg == NULL) {
8674bc52338SCy Schubert 			wpabuf_clear_free(in_decrypted);
86839beb93cSSam Leffler 			return 0;
86939beb93cSSam Leffler 		}
87039beb93cSSam Leffler 		nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
87139beb93cSSam Leffler 		wpabuf_put_buf(nmsg, in_decrypted);
87239beb93cSSam Leffler 		nhdr->code = req->code;
87339beb93cSSam Leffler 		nhdr->identifier = req->identifier;
87439beb93cSSam Leffler 		nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
87539beb93cSSam Leffler 					    wpabuf_len(in_decrypted));
87639beb93cSSam Leffler 
8774bc52338SCy Schubert 		wpabuf_clear_free(in_decrypted);
87839beb93cSSam Leffler 		in_decrypted = nmsg;
87939beb93cSSam Leffler 	}
88039beb93cSSam Leffler 
88139beb93cSSam Leffler 	hdr = wpabuf_mhead(in_decrypted);
88239beb93cSSam Leffler 	if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
88339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
88439beb93cSSam Leffler 			   "EAP frame (len=%lu)",
88539beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted));
8864bc52338SCy Schubert 		wpabuf_clear_free(in_decrypted);
88739beb93cSSam Leffler 		return 0;
88839beb93cSSam Leffler 	}
88939beb93cSSam Leffler 	len = be_to_host16(hdr->length);
89039beb93cSSam Leffler 	if (len > wpabuf_len(in_decrypted)) {
89139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
89239beb93cSSam Leffler 			   "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
89339beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted),
89439beb93cSSam Leffler 			   (unsigned long) len);
8954bc52338SCy Schubert 		wpabuf_clear_free(in_decrypted);
89639beb93cSSam Leffler 		return 0;
89739beb93cSSam Leffler 	}
89839beb93cSSam Leffler 	if (len < wpabuf_len(in_decrypted)) {
89939beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
90039beb93cSSam Leffler 			   "shorter length than full decrypted data "
90139beb93cSSam Leffler 			   "(%lu < %lu)",
90239beb93cSSam Leffler 			   (unsigned long) len,
90339beb93cSSam Leffler 			   (unsigned long) wpabuf_len(in_decrypted));
90439beb93cSSam Leffler 	}
90539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
90639beb93cSSam Leffler 		   "identifier=%d length=%lu", hdr->code, hdr->identifier,
90739beb93cSSam Leffler 		   (unsigned long) len);
90839beb93cSSam Leffler 	switch (hdr->code) {
90939beb93cSSam Leffler 	case EAP_CODE_REQUEST:
91039beb93cSSam Leffler 		if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
91139beb93cSSam Leffler 					    &resp)) {
9124bc52338SCy Schubert 			wpabuf_clear_free(in_decrypted);
91339beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
91439beb93cSSam Leffler 				   "processing failed");
91539beb93cSSam Leffler 			return 0;
91639beb93cSSam Leffler 		}
91739beb93cSSam Leffler 		break;
91839beb93cSSam Leffler 	case EAP_CODE_SUCCESS:
91939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
92039beb93cSSam Leffler 		if (data->peap_version == 1) {
92139beb93cSSam Leffler 			/* EAP-Success within TLS tunnel is used to indicate
92239beb93cSSam Leffler 			 * shutdown of the TLS channel. The authentication has
92339beb93cSSam Leffler 			 * been completed. */
924*a90b9d01SCy Schubert 			if (!peap_phase2_sufficient(sm, data)) {
92539beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
92639beb93cSSam Leffler 					   "Success used to indicate success, "
92739beb93cSSam Leffler 					   "but Phase 2 EAP was not yet "
92839beb93cSSam Leffler 					   "completed successfully");
92939beb93cSSam Leffler 				ret->methodState = METHOD_DONE;
93039beb93cSSam Leffler 				ret->decision = DECISION_FAIL;
9314bc52338SCy Schubert 				wpabuf_clear_free(in_decrypted);
93239beb93cSSam Leffler 				return 0;
93339beb93cSSam Leffler 			}
93439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
93539beb93cSSam Leffler 				   "EAP-Success within TLS tunnel - "
93639beb93cSSam Leffler 				   "authentication completed");
93739beb93cSSam Leffler 			ret->decision = DECISION_UNCOND_SUCC;
93839beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
93939beb93cSSam Leffler 			data->phase2_success = 1;
94039beb93cSSam Leffler 			if (data->peap_outer_success == 2) {
9414bc52338SCy Schubert 				wpabuf_clear_free(in_decrypted);
94239beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
94339beb93cSSam Leffler 					   "to finish authentication");
94439beb93cSSam Leffler 				return 1;
94539beb93cSSam Leffler 			} else if (data->peap_outer_success == 1) {
94639beb93cSSam Leffler 				/* Reply with EAP-Success within the TLS
94739beb93cSSam Leffler 				 * channel to complete the authentication. */
94839beb93cSSam Leffler 				resp = wpabuf_alloc(sizeof(struct eap_hdr));
94939beb93cSSam Leffler 				if (resp) {
95039beb93cSSam Leffler 					rhdr = wpabuf_put(resp, sizeof(*rhdr));
95139beb93cSSam Leffler 					rhdr->code = EAP_CODE_SUCCESS;
95239beb93cSSam Leffler 					rhdr->identifier = hdr->identifier;
95339beb93cSSam Leffler 					rhdr->length =
95439beb93cSSam Leffler 						host_to_be16(sizeof(*rhdr));
95539beb93cSSam Leffler 				}
95639beb93cSSam Leffler 			} else {
95739beb93cSSam Leffler 				/* No EAP-Success expected for Phase 1 (outer,
95839beb93cSSam Leffler 				 * unencrypted auth), so force EAP state
95939beb93cSSam Leffler 				 * machine to SUCCESS state. */
960c1d255d3SCy Schubert 				sm->peap_done = true;
96139beb93cSSam Leffler 			}
96239beb93cSSam Leffler 		} else {
96339beb93cSSam Leffler 			/* FIX: ? */
96439beb93cSSam Leffler 		}
96539beb93cSSam Leffler 		break;
96639beb93cSSam Leffler 	case EAP_CODE_FAILURE:
96739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
96839beb93cSSam Leffler 		ret->decision = DECISION_FAIL;
96939beb93cSSam Leffler 		ret->methodState = METHOD_MAY_CONT;
970c1d255d3SCy Schubert 		ret->allowNotifications = false;
97139beb93cSSam Leffler 		/* Reply with EAP-Failure within the TLS channel to complete
97239beb93cSSam Leffler 		 * failure reporting. */
97339beb93cSSam Leffler 		resp = wpabuf_alloc(sizeof(struct eap_hdr));
97439beb93cSSam Leffler 		if (resp) {
97539beb93cSSam Leffler 			rhdr = wpabuf_put(resp, sizeof(*rhdr));
97639beb93cSSam Leffler 			rhdr->code = EAP_CODE_FAILURE;
97739beb93cSSam Leffler 			rhdr->identifier = hdr->identifier;
97839beb93cSSam Leffler 			rhdr->length = host_to_be16(sizeof(*rhdr));
97939beb93cSSam Leffler 		}
98039beb93cSSam Leffler 		break;
98139beb93cSSam Leffler 	default:
98239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
98339beb93cSSam Leffler 			   "Phase 2 EAP header", hdr->code);
98439beb93cSSam Leffler 		break;
98539beb93cSSam Leffler 	}
98639beb93cSSam Leffler 
9874bc52338SCy Schubert 	wpabuf_clear_free(in_decrypted);
98839beb93cSSam Leffler 
98939beb93cSSam Leffler 	if (resp) {
99039beb93cSSam Leffler 		int skip_change2 = 0;
99139beb93cSSam Leffler 		struct wpabuf *rmsg, buf;
99239beb93cSSam Leffler 
99339beb93cSSam Leffler 		wpa_hexdump_buf_key(MSG_DEBUG,
99439beb93cSSam Leffler 				    "EAP-PEAP: Encrypting Phase 2 data", resp);
99539beb93cSSam Leffler 		/* PEAP version changes */
99639beb93cSSam Leffler 		if (wpabuf_len(resp) >= 5 &&
99739beb93cSSam Leffler 		    wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
99839beb93cSSam Leffler 		    eap_get_type(resp) == EAP_TYPE_TLV)
99939beb93cSSam Leffler 			skip_change2 = 1;
100039beb93cSSam Leffler 		rmsg = resp;
100139beb93cSSam Leffler 		if (data->peap_version == 0 && !skip_change2) {
100239beb93cSSam Leffler 			wpabuf_set(&buf, wpabuf_head_u8(resp) +
100339beb93cSSam Leffler 				   sizeof(struct eap_hdr),
100439beb93cSSam Leffler 				   wpabuf_len(resp) - sizeof(struct eap_hdr));
100539beb93cSSam Leffler 			rmsg = &buf;
100639beb93cSSam Leffler 		}
100739beb93cSSam Leffler 
100839beb93cSSam Leffler 		if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
100939beb93cSSam Leffler 					 data->peap_version, req->identifier,
101039beb93cSSam Leffler 					 rmsg, out_data)) {
101139beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
101239beb93cSSam Leffler 				   "a Phase 2 frame");
101339beb93cSSam Leffler 		}
10144bc52338SCy Schubert 		wpabuf_clear_free(resp);
101539beb93cSSam Leffler 	}
101639beb93cSSam Leffler 
101739beb93cSSam Leffler 	return 0;
101839beb93cSSam Leffler }
101939beb93cSSam Leffler 
102039beb93cSSam Leffler 
102139beb93cSSam Leffler static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
102239beb93cSSam Leffler 					struct eap_method_ret *ret,
102339beb93cSSam Leffler 					const struct wpabuf *reqData)
102439beb93cSSam Leffler {
102539beb93cSSam Leffler 	const struct eap_hdr *req;
102639beb93cSSam Leffler 	size_t left;
102739beb93cSSam Leffler 	int res;
102839beb93cSSam Leffler 	u8 flags, id;
102939beb93cSSam Leffler 	struct wpabuf *resp;
103039beb93cSSam Leffler 	const u8 *pos;
103139beb93cSSam Leffler 	struct eap_peap_data *data = priv;
1032325151a3SRui Paulo 	struct wpabuf msg;
103339beb93cSSam Leffler 
103439beb93cSSam Leffler 	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
103539beb93cSSam Leffler 					reqData, &left, &flags);
103639beb93cSSam Leffler 	if (pos == NULL)
103739beb93cSSam Leffler 		return NULL;
103839beb93cSSam Leffler 	req = wpabuf_head(reqData);
103939beb93cSSam Leffler 	id = req->identifier;
104039beb93cSSam Leffler 
104139beb93cSSam Leffler 	if (flags & EAP_TLS_FLAGS_START) {
104239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
1043e28a4053SRui Paulo 			   "ver=%d)", flags & EAP_TLS_VERSION_MASK,
104439beb93cSSam Leffler 			data->peap_version);
1045e28a4053SRui Paulo 		if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
1046e28a4053SRui Paulo 			data->peap_version = flags & EAP_TLS_VERSION_MASK;
104739beb93cSSam Leffler 		if (data->force_peap_version >= 0 &&
104839beb93cSSam Leffler 		    data->force_peap_version != data->peap_version) {
104939beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
105039beb93cSSam Leffler 				   "forced PEAP version %d",
105139beb93cSSam Leffler 				   data->force_peap_version);
105239beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
105339beb93cSSam Leffler 			ret->decision = DECISION_FAIL;
1054c1d255d3SCy Schubert 			ret->allowNotifications = false;
105539beb93cSSam Leffler 			return NULL;
105639beb93cSSam Leffler 		}
105739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
105839beb93cSSam Leffler 			   data->peap_version);
105939beb93cSSam Leffler 		left = 0; /* make sure that this frame is empty, even though it
106039beb93cSSam Leffler 			   * should always be, anyway */
106139beb93cSSam Leffler 	}
106239beb93cSSam Leffler 
1063325151a3SRui Paulo 	wpabuf_set(&msg, pos, left);
1064325151a3SRui Paulo 
106539beb93cSSam Leffler 	resp = NULL;
106639beb93cSSam Leffler 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
106739beb93cSSam Leffler 	    !data->resuming) {
106839beb93cSSam Leffler 		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
106939beb93cSSam Leffler 	} else {
1070780fb4a2SCy Schubert 		if (sm->waiting_ext_cert_check && data->pending_resp) {
1071780fb4a2SCy Schubert 			struct eap_peer_config *config = eap_get_config(sm);
1072780fb4a2SCy Schubert 
1073780fb4a2SCy Schubert 			if (config->pending_ext_cert_check ==
1074780fb4a2SCy Schubert 			    EXT_CERT_CHECK_GOOD) {
1075780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG,
1076780fb4a2SCy Schubert 					   "EAP-PEAP: External certificate check succeeded - continue handshake");
1077780fb4a2SCy Schubert 				resp = data->pending_resp;
1078780fb4a2SCy Schubert 				data->pending_resp = NULL;
1079780fb4a2SCy Schubert 				sm->waiting_ext_cert_check = 0;
1080780fb4a2SCy Schubert 				return resp;
1081780fb4a2SCy Schubert 			}
1082780fb4a2SCy Schubert 
1083780fb4a2SCy Schubert 			if (config->pending_ext_cert_check ==
1084780fb4a2SCy Schubert 			    EXT_CERT_CHECK_BAD) {
1085780fb4a2SCy Schubert 				wpa_printf(MSG_DEBUG,
1086780fb4a2SCy Schubert 					   "EAP-PEAP: External certificate check failed - force authentication failure");
1087780fb4a2SCy Schubert 				ret->methodState = METHOD_DONE;
1088780fb4a2SCy Schubert 				ret->decision = DECISION_FAIL;
1089780fb4a2SCy Schubert 				sm->waiting_ext_cert_check = 0;
1090780fb4a2SCy Schubert 				return NULL;
1091780fb4a2SCy Schubert 			}
1092780fb4a2SCy Schubert 
1093780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
1094780fb4a2SCy Schubert 				   "EAP-PEAP: Continuing to wait external server certificate validation");
1095780fb4a2SCy Schubert 			return NULL;
1096780fb4a2SCy Schubert 		}
1097780fb4a2SCy Schubert 
109839beb93cSSam Leffler 		res = eap_peer_tls_process_helper(sm, &data->ssl,
109939beb93cSSam Leffler 						  EAP_TYPE_PEAP,
1100325151a3SRui Paulo 						  data->peap_version, id, &msg,
1101325151a3SRui Paulo 						  &resp);
110239beb93cSSam Leffler 
1103325151a3SRui Paulo 		if (res < 0) {
1104325151a3SRui Paulo 			wpa_printf(MSG_DEBUG,
1105325151a3SRui Paulo 				   "EAP-PEAP: TLS processing failed");
1106325151a3SRui Paulo 			ret->methodState = METHOD_DONE;
1107325151a3SRui Paulo 			ret->decision = DECISION_FAIL;
1108325151a3SRui Paulo 			return resp;
1109325151a3SRui Paulo 		}
1110780fb4a2SCy Schubert 
1111780fb4a2SCy Schubert 
1112780fb4a2SCy Schubert 		if (sm->waiting_ext_cert_check) {
1113780fb4a2SCy Schubert 			wpa_printf(MSG_DEBUG,
1114780fb4a2SCy Schubert 				   "EAP-PEAP: Waiting external server certificate validation");
11154bc52338SCy Schubert 			wpabuf_clear_free(data->pending_resp);
1116780fb4a2SCy Schubert 			data->pending_resp = resp;
1117780fb4a2SCy Schubert 			return NULL;
1118780fb4a2SCy Schubert 		}
1119780fb4a2SCy Schubert 
112039beb93cSSam Leffler 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
1121c1d255d3SCy Schubert 			const char *label;
1122c1d255d3SCy Schubert 			const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP };
1123c1d255d3SCy Schubert 			const u8 *context = NULL;
1124c1d255d3SCy Schubert 			size_t context_len = 0;
1125c1d255d3SCy Schubert 
112639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG,
112739beb93cSSam Leffler 				   "EAP-PEAP: TLS done, proceed to Phase 2");
11285b9c547cSRui Paulo 			eap_peap_free_key(data);
112939beb93cSSam Leffler 			/* draft-josefsson-ppext-eap-tls-eap-05.txt
113039beb93cSSam Leffler 			 * specifies that PEAPv1 would use "client PEAP
113139beb93cSSam Leffler 			 * encryption" as the label. However, most existing
113239beb93cSSam Leffler 			 * PEAPv1 implementations seem to be using the old
113339beb93cSSam Leffler 			 * label, "client EAP encryption", instead. Use the old
113439beb93cSSam Leffler 			 * label by default, but allow it to be configured with
1135c1d255d3SCy Schubert 			 * phase1 parameter peaplabel=1.
1136c1d255d3SCy Schubert 			 *
1137c1d255d3SCy Schubert 			 * When using TLS 1.3, draft-ietf-emu-tls-eap-types
1138c1d255d3SCy Schubert 			 * defines a new set of label and context parameters.
1139c1d255d3SCy Schubert 			 */
1140c1d255d3SCy Schubert 			if (data->ssl.tls_v13) {
1141c1d255d3SCy Schubert 				label = "EXPORTER_EAP_TLS_Key_Material";
1142c1d255d3SCy Schubert 				context = eap_tls13_context;
1143c1d255d3SCy Schubert 				context_len = sizeof(eap_tls13_context);
1144c1d255d3SCy Schubert 			} else if (data->force_new_label) {
114539beb93cSSam Leffler 				label = "client PEAP encryption";
1146c1d255d3SCy Schubert 			} else {
114739beb93cSSam Leffler 				label = "client EAP encryption";
1148c1d255d3SCy Schubert 			}
114939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
115039beb93cSSam Leffler 				   "key derivation", label);
115139beb93cSSam Leffler 			data->key_data =
115239beb93cSSam Leffler 				eap_peer_tls_derive_key(sm, &data->ssl, label,
1153c1d255d3SCy Schubert 							context, context_len,
11544bc52338SCy Schubert 							EAP_TLS_KEY_LEN +
11554bc52338SCy Schubert 							EAP_EMSK_LEN);
115639beb93cSSam Leffler 			if (data->key_data) {
115739beb93cSSam Leffler 				wpa_hexdump_key(MSG_DEBUG,
115839beb93cSSam Leffler 						"EAP-PEAP: Derived key",
115939beb93cSSam Leffler 						data->key_data,
116039beb93cSSam Leffler 						EAP_TLS_KEY_LEN);
11614bc52338SCy Schubert 				wpa_hexdump_key(MSG_DEBUG,
11624bc52338SCy Schubert 						"EAP-PEAP: Derived EMSK",
11634bc52338SCy Schubert 						data->key_data +
11644bc52338SCy Schubert 						EAP_TLS_KEY_LEN,
11654bc52338SCy Schubert 						EAP_EMSK_LEN);
116639beb93cSSam Leffler 			} else {
116739beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
116839beb93cSSam Leffler 					   "derive key");
116939beb93cSSam Leffler 			}
117039beb93cSSam Leffler 
11715b9c547cSRui Paulo 			os_free(data->session_id);
11725b9c547cSRui Paulo 			data->session_id =
11735b9c547cSRui Paulo 				eap_peer_tls_derive_session_id(sm, &data->ssl,
11745b9c547cSRui Paulo 							       EAP_TYPE_PEAP,
11755b9c547cSRui Paulo 							       &data->id_len);
11765b9c547cSRui Paulo 			if (data->session_id) {
11775b9c547cSRui Paulo 				wpa_hexdump(MSG_DEBUG,
11785b9c547cSRui Paulo 					    "EAP-PEAP: Derived Session-Id",
11795b9c547cSRui Paulo 					    data->session_id, data->id_len);
11805b9c547cSRui Paulo 			} else {
11815b9c547cSRui Paulo 				wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
11825b9c547cSRui Paulo 					   "derive Session-Id");
11835b9c547cSRui Paulo 			}
11845b9c547cSRui Paulo 
118539beb93cSSam Leffler 			if (sm->workaround && data->resuming) {
118639beb93cSSam Leffler 				/*
118739beb93cSSam Leffler 				 * At least few RADIUS servers (Aegis v1.1.6;
118839beb93cSSam Leffler 				 * but not v1.1.4; and Cisco ACS) seem to be
118939beb93cSSam Leffler 				 * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
119039beb93cSSam Leffler 				 * ACS) session resumption with outer
119139beb93cSSam Leffler 				 * EAP-Success. This does not seem to follow
119239beb93cSSam Leffler 				 * draft-josefsson-pppext-eap-tls-eap-05.txt
119339beb93cSSam Leffler 				 * section 4.2, so only allow this if EAP
119439beb93cSSam Leffler 				 * workarounds are enabled.
119539beb93cSSam Leffler 				 */
119639beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
119739beb93cSSam Leffler 					   "allow outer EAP-Success to "
119839beb93cSSam Leffler 					   "terminate PEAP resumption");
119939beb93cSSam Leffler 				ret->decision = DECISION_COND_SUCC;
120039beb93cSSam Leffler 				data->phase2_success = 1;
120139beb93cSSam Leffler 			}
120239beb93cSSam Leffler 
120339beb93cSSam Leffler 			data->resuming = 0;
120439beb93cSSam Leffler 		}
120539beb93cSSam Leffler 
120639beb93cSSam Leffler 		if (res == 2) {
120739beb93cSSam Leffler 			/*
120839beb93cSSam Leffler 			 * Application data included in the handshake message.
120939beb93cSSam Leffler 			 */
12104bc52338SCy Schubert 			wpabuf_clear_free(data->pending_phase2_req);
121139beb93cSSam Leffler 			data->pending_phase2_req = resp;
121239beb93cSSam Leffler 			resp = NULL;
121339beb93cSSam Leffler 			res = eap_peap_decrypt(sm, data, ret, req, &msg,
121439beb93cSSam Leffler 					       &resp);
121539beb93cSSam Leffler 		}
121639beb93cSSam Leffler 	}
121739beb93cSSam Leffler 
121839beb93cSSam Leffler 	if (ret->methodState == METHOD_DONE) {
1219c1d255d3SCy Schubert 		ret->allowNotifications = false;
122039beb93cSSam Leffler 	}
122139beb93cSSam Leffler 
122239beb93cSSam Leffler 	if (res == 1) {
12234bc52338SCy Schubert 		wpabuf_clear_free(resp);
122439beb93cSSam Leffler 		return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
122539beb93cSSam Leffler 					      data->peap_version);
122639beb93cSSam Leffler 	}
122739beb93cSSam Leffler 
122839beb93cSSam Leffler 	return resp;
122939beb93cSSam Leffler }
123039beb93cSSam Leffler 
123139beb93cSSam Leffler 
1232c1d255d3SCy Schubert static bool eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
123339beb93cSSam Leffler {
123439beb93cSSam Leffler 	struct eap_peap_data *data = priv;
1235*a90b9d01SCy Schubert 
123639beb93cSSam Leffler 	return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
1237*a90b9d01SCy Schubert 		data->phase2_success && data->phase2_auth != ALWAYS;
123839beb93cSSam Leffler }
123939beb93cSSam Leffler 
124039beb93cSSam Leffler 
124139beb93cSSam Leffler static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
124239beb93cSSam Leffler {
124339beb93cSSam Leffler 	struct eap_peap_data *data = priv;
124485732ac8SCy Schubert 
124585732ac8SCy Schubert 	if (data->phase2_priv && data->phase2_method &&
124685732ac8SCy Schubert 	    data->phase2_method->deinit_for_reauth)
124785732ac8SCy Schubert 		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
12484bc52338SCy Schubert 	wpabuf_clear_free(data->pending_phase2_req);
124939beb93cSSam Leffler 	data->pending_phase2_req = NULL;
12504bc52338SCy Schubert 	wpabuf_clear_free(data->pending_resp);
1251780fb4a2SCy Schubert 	data->pending_resp = NULL;
125239beb93cSSam Leffler 	data->crypto_binding_used = 0;
125339beb93cSSam Leffler }
125439beb93cSSam Leffler 
125539beb93cSSam Leffler 
125639beb93cSSam Leffler static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
125739beb93cSSam Leffler {
125839beb93cSSam Leffler 	struct eap_peap_data *data = priv;
12595b9c547cSRui Paulo 	eap_peap_free_key(data);
12605b9c547cSRui Paulo 	os_free(data->session_id);
12615b9c547cSRui Paulo 	data->session_id = NULL;
126239beb93cSSam Leffler 	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
126339beb93cSSam Leffler 		os_free(data);
126439beb93cSSam Leffler 		return NULL;
126539beb93cSSam Leffler 	}
126639beb93cSSam Leffler 	if (data->phase2_priv && data->phase2_method &&
126739beb93cSSam Leffler 	    data->phase2_method->init_for_reauth)
126839beb93cSSam Leffler 		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
126939beb93cSSam Leffler 	data->phase2_success = 0;
127039beb93cSSam Leffler 	data->phase2_eap_success = 0;
127139beb93cSSam Leffler 	data->phase2_eap_started = 0;
127239beb93cSSam Leffler 	data->resuming = 1;
127339beb93cSSam Leffler 	data->reauth = 1;
1274c1d255d3SCy Schubert 	sm->peap_done = false;
127539beb93cSSam Leffler 	return priv;
127639beb93cSSam Leffler }
127739beb93cSSam Leffler 
127839beb93cSSam Leffler 
127939beb93cSSam Leffler static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
128039beb93cSSam Leffler 			       size_t buflen, int verbose)
128139beb93cSSam Leffler {
128239beb93cSSam Leffler 	struct eap_peap_data *data = priv;
128339beb93cSSam Leffler 	int len, ret;
128439beb93cSSam Leffler 
128539beb93cSSam Leffler 	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
128639beb93cSSam Leffler 	if (data->phase2_method) {
128739beb93cSSam Leffler 		ret = os_snprintf(buf + len, buflen - len,
128839beb93cSSam Leffler 				  "EAP-PEAPv%d Phase2 method=%s\n",
128939beb93cSSam Leffler 				  data->peap_version,
129039beb93cSSam Leffler 				  data->phase2_method->name);
12915b9c547cSRui Paulo 		if (os_snprintf_error(buflen - len, ret))
129239beb93cSSam Leffler 			return len;
129339beb93cSSam Leffler 		len += ret;
129439beb93cSSam Leffler 	}
129539beb93cSSam Leffler 	return len;
129639beb93cSSam Leffler }
129739beb93cSSam Leffler 
129839beb93cSSam Leffler 
1299c1d255d3SCy Schubert static bool eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
130039beb93cSSam Leffler {
130139beb93cSSam Leffler 	struct eap_peap_data *data = priv;
130239beb93cSSam Leffler 	return data->key_data != NULL && data->phase2_success;
130339beb93cSSam Leffler }
130439beb93cSSam Leffler 
130539beb93cSSam Leffler 
130639beb93cSSam Leffler static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
130739beb93cSSam Leffler {
130839beb93cSSam Leffler 	struct eap_peap_data *data = priv;
130939beb93cSSam Leffler 	u8 *key;
131039beb93cSSam Leffler 
131139beb93cSSam Leffler 	if (data->key_data == NULL || !data->phase2_success)
131239beb93cSSam Leffler 		return NULL;
131339beb93cSSam Leffler 
131439beb93cSSam Leffler 	key = os_malloc(EAP_TLS_KEY_LEN);
131539beb93cSSam Leffler 	if (key == NULL)
131639beb93cSSam Leffler 		return NULL;
131739beb93cSSam Leffler 
131839beb93cSSam Leffler 	*len = EAP_TLS_KEY_LEN;
131939beb93cSSam Leffler 
132039beb93cSSam Leffler 	if (data->crypto_binding_used) {
132139beb93cSSam Leffler 		u8 csk[128];
132239beb93cSSam Leffler 		/*
132339beb93cSSam Leffler 		 * Note: It looks like Microsoft implementation requires null
132439beb93cSSam Leffler 		 * termination for this label while the one used for deriving
132539beb93cSSam Leffler 		 * IPMK|CMK did not use null termination.
132639beb93cSSam Leffler 		 */
1327f05cddf9SRui Paulo 		if (peap_prfplus(data->peap_version, data->ipmk, 40,
132839beb93cSSam Leffler 				 "Session Key Generating Function",
1329f05cddf9SRui Paulo 				 (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
1330f05cddf9SRui Paulo 			os_free(key);
1331f05cddf9SRui Paulo 			return NULL;
1332f05cddf9SRui Paulo 		}
133339beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
133439beb93cSSam Leffler 		os_memcpy(key, csk, EAP_TLS_KEY_LEN);
133539beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
133639beb93cSSam Leffler 			    key, EAP_TLS_KEY_LEN);
1337206b73d0SCy Schubert 		forced_memzero(csk, sizeof(csk));
133839beb93cSSam Leffler 	} else
133939beb93cSSam Leffler 		os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
134039beb93cSSam Leffler 
134139beb93cSSam Leffler 	return key;
134239beb93cSSam Leffler }
134339beb93cSSam Leffler 
134439beb93cSSam Leffler 
13454bc52338SCy Schubert static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
13464bc52338SCy Schubert {
13474bc52338SCy Schubert 	struct eap_peap_data *data = priv;
13484bc52338SCy Schubert 	u8 *key;
13494bc52338SCy Schubert 
13504bc52338SCy Schubert 	if (!data->key_data || !data->phase2_success)
13514bc52338SCy Schubert 		return NULL;
13524bc52338SCy Schubert 
13534bc52338SCy Schubert 	if (data->crypto_binding_used) {
13544bc52338SCy Schubert 		/* [MS-PEAP] does not define EMSK derivation */
13554bc52338SCy Schubert 		return NULL;
13564bc52338SCy Schubert 	}
13574bc52338SCy Schubert 
13584bc52338SCy Schubert 	key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
13594bc52338SCy Schubert 	if (!key)
13604bc52338SCy Schubert 		return NULL;
13614bc52338SCy Schubert 
13624bc52338SCy Schubert 	*len = EAP_EMSK_LEN;
13634bc52338SCy Schubert 
13644bc52338SCy Schubert 	return key;
13654bc52338SCy Schubert }
13664bc52338SCy Schubert 
13674bc52338SCy Schubert 
13685b9c547cSRui Paulo static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
13695b9c547cSRui Paulo {
13705b9c547cSRui Paulo 	struct eap_peap_data *data = priv;
13715b9c547cSRui Paulo 	u8 *id;
13725b9c547cSRui Paulo 
13735b9c547cSRui Paulo 	if (data->session_id == NULL || !data->phase2_success)
13745b9c547cSRui Paulo 		return NULL;
13755b9c547cSRui Paulo 
137685732ac8SCy Schubert 	id = os_memdup(data->session_id, data->id_len);
13775b9c547cSRui Paulo 	if (id == NULL)
13785b9c547cSRui Paulo 		return NULL;
13795b9c547cSRui Paulo 
13805b9c547cSRui Paulo 	*len = data->id_len;
13815b9c547cSRui Paulo 
13825b9c547cSRui Paulo 	return id;
13835b9c547cSRui Paulo }
13845b9c547cSRui Paulo 
13855b9c547cSRui Paulo 
138639beb93cSSam Leffler int eap_peer_peap_register(void)
138739beb93cSSam Leffler {
138839beb93cSSam Leffler 	struct eap_method *eap;
138939beb93cSSam Leffler 
139039beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
139139beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
139239beb93cSSam Leffler 	if (eap == NULL)
139339beb93cSSam Leffler 		return -1;
139439beb93cSSam Leffler 
139539beb93cSSam Leffler 	eap->init = eap_peap_init;
139639beb93cSSam Leffler 	eap->deinit = eap_peap_deinit;
139739beb93cSSam Leffler 	eap->process = eap_peap_process;
139839beb93cSSam Leffler 	eap->isKeyAvailable = eap_peap_isKeyAvailable;
139939beb93cSSam Leffler 	eap->getKey = eap_peap_getKey;
14004bc52338SCy Schubert 	eap->get_emsk = eap_peap_get_emsk;
140139beb93cSSam Leffler 	eap->get_status = eap_peap_get_status;
140239beb93cSSam Leffler 	eap->has_reauth_data = eap_peap_has_reauth_data;
140339beb93cSSam Leffler 	eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
140439beb93cSSam Leffler 	eap->init_for_reauth = eap_peap_init_for_reauth;
14055b9c547cSRui Paulo 	eap->getSessionId = eap_peap_get_session_id;
140639beb93cSSam Leffler 
1407780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
140839beb93cSSam Leffler }
1409