xref: /freebsd/contrib/wpa/src/eap_peer/eap_wsc.c (revision c1d255d3)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAP-WSC peer for Wi-Fi Protected Setup
3f05cddf9SRui Paulo  * Copyright (c) 2007-2009, 2012, 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 "uuid.h"
1339beb93cSSam Leffler #include "eap_i.h"
1439beb93cSSam Leffler #include "eap_common/eap_wsc_common.h"
1539beb93cSSam Leffler #include "wps/wps.h"
1639beb93cSSam Leffler #include "wps/wps_defs.h"
1739beb93cSSam Leffler 
1839beb93cSSam Leffler 
1939beb93cSSam Leffler struct eap_wsc_data {
20780fb4a2SCy Schubert 	enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state;
2139beb93cSSam Leffler 	int registrar;
2239beb93cSSam Leffler 	struct wpabuf *in_buf;
2339beb93cSSam Leffler 	struct wpabuf *out_buf;
2439beb93cSSam Leffler 	enum wsc_op_code in_op_code, out_op_code;
2539beb93cSSam Leffler 	size_t out_used;
2639beb93cSSam Leffler 	size_t fragment_size;
2739beb93cSSam Leffler 	struct wps_data *wps;
2839beb93cSSam Leffler 	struct wps_context *wps_ctx;
2939beb93cSSam Leffler };
3039beb93cSSam Leffler 
3139beb93cSSam Leffler 
eap_wsc_state_txt(int state)3239beb93cSSam Leffler static const char * eap_wsc_state_txt(int state)
3339beb93cSSam Leffler {
3439beb93cSSam Leffler 	switch (state) {
3539beb93cSSam Leffler 	case WAIT_START:
3639beb93cSSam Leffler 		return "WAIT_START";
3739beb93cSSam Leffler 	case MESG:
3839beb93cSSam Leffler 		return "MESG";
3939beb93cSSam Leffler 	case WAIT_FRAG_ACK:
4039beb93cSSam Leffler 		return "WAIT_FRAG_ACK";
4139beb93cSSam Leffler 	case FAIL:
4239beb93cSSam Leffler 		return "FAIL";
4339beb93cSSam Leffler 	default:
4439beb93cSSam Leffler 		return "?";
4539beb93cSSam Leffler 	}
4639beb93cSSam Leffler }
4739beb93cSSam Leffler 
4839beb93cSSam Leffler 
eap_wsc_state(struct eap_wsc_data * data,int state)4939beb93cSSam Leffler static void eap_wsc_state(struct eap_wsc_data *data, int state)
5039beb93cSSam Leffler {
5139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
5239beb93cSSam Leffler 		   eap_wsc_state_txt(data->state),
5339beb93cSSam Leffler 		   eap_wsc_state_txt(state));
5439beb93cSSam Leffler 	data->state = state;
5539beb93cSSam Leffler }
5639beb93cSSam Leffler 
5739beb93cSSam Leffler 
eap_wsc_new_ap_settings(struct wps_credential * cred,const char * params)58e28a4053SRui Paulo static int eap_wsc_new_ap_settings(struct wps_credential *cred,
59e28a4053SRui Paulo 				   const char *params)
60e28a4053SRui Paulo {
61e28a4053SRui Paulo 	const char *pos, *end;
62e28a4053SRui Paulo 	size_t len;
63e28a4053SRui Paulo 
64e28a4053SRui Paulo 	os_memset(cred, 0, sizeof(*cred));
65e28a4053SRui Paulo 
66e28a4053SRui Paulo 	pos = os_strstr(params, "new_ssid=");
67e28a4053SRui Paulo 	if (pos == NULL)
68e28a4053SRui Paulo 		return 0;
69e28a4053SRui Paulo 	pos += 9;
70e28a4053SRui Paulo 	end = os_strchr(pos, ' ');
71e28a4053SRui Paulo 	if (end == NULL)
72e28a4053SRui Paulo 		len = os_strlen(pos);
73e28a4053SRui Paulo 	else
74e28a4053SRui Paulo 		len = end - pos;
75e28a4053SRui Paulo 	if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
765b9c547cSRui Paulo 	    hexstr2bin(pos, cred->ssid, len / 2)) {
775b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
78e28a4053SRui Paulo 		return -1;
795b9c547cSRui Paulo 	}
80e28a4053SRui Paulo 	cred->ssid_len = len / 2;
81e28a4053SRui Paulo 
82e28a4053SRui Paulo 	pos = os_strstr(params, "new_auth=");
835b9c547cSRui Paulo 	if (pos == NULL) {
845b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
85e28a4053SRui Paulo 		return -1;
865b9c547cSRui Paulo 	}
87e28a4053SRui Paulo 	if (os_strncmp(pos + 9, "OPEN", 4) == 0)
88e28a4053SRui Paulo 		cred->auth_type = WPS_AUTH_OPEN;
89e28a4053SRui Paulo 	else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
90e28a4053SRui Paulo 		cred->auth_type = WPS_AUTH_WPAPSK;
91e28a4053SRui Paulo 	else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
92e28a4053SRui Paulo 		cred->auth_type = WPS_AUTH_WPA2PSK;
935b9c547cSRui Paulo 	else {
945b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
95e28a4053SRui Paulo 		return -1;
965b9c547cSRui Paulo 	}
97e28a4053SRui Paulo 
98e28a4053SRui Paulo 	pos = os_strstr(params, "new_encr=");
995b9c547cSRui Paulo 	if (pos == NULL) {
1005b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
101e28a4053SRui Paulo 		return -1;
1025b9c547cSRui Paulo 	}
103e28a4053SRui Paulo 	if (os_strncmp(pos + 9, "NONE", 4) == 0)
104e28a4053SRui Paulo 		cred->encr_type = WPS_ENCR_NONE;
1055b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
106e28a4053SRui Paulo 	else if (os_strncmp(pos + 9, "WEP", 3) == 0)
107e28a4053SRui Paulo 		cred->encr_type = WPS_ENCR_WEP;
1085b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
109e28a4053SRui Paulo 	else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
110e28a4053SRui Paulo 		cred->encr_type = WPS_ENCR_TKIP;
111e28a4053SRui Paulo 	else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
112e28a4053SRui Paulo 		cred->encr_type = WPS_ENCR_AES;
1135b9c547cSRui Paulo 	else {
1145b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
115e28a4053SRui Paulo 		return -1;
1165b9c547cSRui Paulo 	}
117e28a4053SRui Paulo 
118e28a4053SRui Paulo 	pos = os_strstr(params, "new_key=");
119e28a4053SRui Paulo 	if (pos == NULL)
120e28a4053SRui Paulo 		return 0;
121e28a4053SRui Paulo 	pos += 8;
122e28a4053SRui Paulo 	end = os_strchr(pos, ' ');
123e28a4053SRui Paulo 	if (end == NULL)
124e28a4053SRui Paulo 		len = os_strlen(pos);
125e28a4053SRui Paulo 	else
126e28a4053SRui Paulo 		len = end - pos;
127e28a4053SRui Paulo 	if ((len & 1) || len > 2 * sizeof(cred->key) ||
1285b9c547cSRui Paulo 	    hexstr2bin(pos, cred->key, len / 2)) {
1295b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
130e28a4053SRui Paulo 		return -1;
1315b9c547cSRui Paulo 	}
132e28a4053SRui Paulo 	cred->key_len = len / 2;
133e28a4053SRui Paulo 
134e28a4053SRui Paulo 	return 1;
135e28a4053SRui Paulo }
136e28a4053SRui Paulo 
137e28a4053SRui Paulo 
eap_wsc_init(struct eap_sm * sm)13839beb93cSSam Leffler static void * eap_wsc_init(struct eap_sm *sm)
13939beb93cSSam Leffler {
14039beb93cSSam Leffler 	struct eap_wsc_data *data;
14139beb93cSSam Leffler 	const u8 *identity;
14239beb93cSSam Leffler 	size_t identity_len;
14339beb93cSSam Leffler 	int registrar;
14439beb93cSSam Leffler 	struct wps_config cfg;
1455b9c547cSRui Paulo 	const char *pos, *end;
14639beb93cSSam Leffler 	const char *phase1;
14739beb93cSSam Leffler 	struct wps_context *wps;
148e28a4053SRui Paulo 	struct wps_credential new_ap_settings;
149e28a4053SRui Paulo 	int res;
150f05cddf9SRui Paulo 	int nfc = 0;
1515b9c547cSRui Paulo 	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
15239beb93cSSam Leffler 
15339beb93cSSam Leffler 	wps = sm->wps;
15439beb93cSSam Leffler 	if (wps == NULL) {
15539beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
15639beb93cSSam Leffler 		return NULL;
15739beb93cSSam Leffler 	}
15839beb93cSSam Leffler 
15939beb93cSSam Leffler 	identity = eap_get_config_identity(sm, &identity_len);
16039beb93cSSam Leffler 
16139beb93cSSam Leffler 	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
16239beb93cSSam Leffler 	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
16339beb93cSSam Leffler 		registrar = 1; /* Supplicant is Registrar */
16439beb93cSSam Leffler 	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
16539beb93cSSam Leffler 	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
16639beb93cSSam Leffler 		registrar = 0; /* Supplicant is Enrollee */
16739beb93cSSam Leffler 	else {
16839beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
16939beb93cSSam Leffler 				  identity, identity_len);
17039beb93cSSam Leffler 		return NULL;
17139beb93cSSam Leffler 	}
17239beb93cSSam Leffler 
17339beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
17439beb93cSSam Leffler 	if (data == NULL)
17539beb93cSSam Leffler 		return NULL;
17639beb93cSSam Leffler 	data->state = registrar ? MESG : WAIT_START;
17739beb93cSSam Leffler 	data->registrar = registrar;
17839beb93cSSam Leffler 	data->wps_ctx = wps;
17939beb93cSSam Leffler 
18039beb93cSSam Leffler 	os_memset(&cfg, 0, sizeof(cfg));
18139beb93cSSam Leffler 	cfg.wps = wps;
18239beb93cSSam Leffler 	cfg.registrar = registrar;
18339beb93cSSam Leffler 
18439beb93cSSam Leffler 	phase1 = eap_get_config_phase1(sm);
18539beb93cSSam Leffler 	if (phase1 == NULL) {
18639beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
18739beb93cSSam Leffler 			   "set");
18839beb93cSSam Leffler 		os_free(data);
18939beb93cSSam Leffler 		return NULL;
19039beb93cSSam Leffler 	}
19139beb93cSSam Leffler 
19239beb93cSSam Leffler 	pos = os_strstr(phase1, "pin=");
19339beb93cSSam Leffler 	if (pos) {
19439beb93cSSam Leffler 		pos += 4;
19539beb93cSSam Leffler 		cfg.pin = (const u8 *) pos;
19639beb93cSSam Leffler 		while (*pos != '\0' && *pos != ' ')
19739beb93cSSam Leffler 			pos++;
19839beb93cSSam Leffler 		cfg.pin_len = pos - (const char *) cfg.pin;
1995b9c547cSRui Paulo 		if (cfg.pin_len == 6 &&
2005b9c547cSRui Paulo 		    os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
201f05cddf9SRui Paulo 			cfg.pin = NULL;
202f05cddf9SRui Paulo 			cfg.pin_len = 0;
203f05cddf9SRui Paulo 			nfc = 1;
204f05cddf9SRui Paulo 		}
20539beb93cSSam Leffler 	} else {
20639beb93cSSam Leffler 		pos = os_strstr(phase1, "pbc=1");
20739beb93cSSam Leffler 		if (pos)
20839beb93cSSam Leffler 			cfg.pbc = 1;
20939beb93cSSam Leffler 	}
21039beb93cSSam Leffler 
2115b9c547cSRui Paulo 	pos = os_strstr(phase1, "dev_pw_id=");
2125b9c547cSRui Paulo 	if (pos) {
2135b9c547cSRui Paulo 		u16 id = atoi(pos + 10);
2145b9c547cSRui Paulo 		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
2155b9c547cSRui Paulo 			nfc = 1;
2165b9c547cSRui Paulo 		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
2175b9c547cSRui Paulo 			cfg.dev_pw_id = id;
2185b9c547cSRui Paulo 	}
2195b9c547cSRui Paulo 
220f05cddf9SRui Paulo 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
22139beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
22239beb93cSSam Leffler 			   "configuration data");
22339beb93cSSam Leffler 		os_free(data);
22439beb93cSSam Leffler 		return NULL;
22539beb93cSSam Leffler 	}
22639beb93cSSam Leffler 
2275b9c547cSRui Paulo 	pos = os_strstr(phase1, " pkhash=");
2285b9c547cSRui Paulo 	if (pos) {
2295b9c547cSRui Paulo 		size_t len;
2305b9c547cSRui Paulo 		pos += 8;
2315b9c547cSRui Paulo 		end = os_strchr(pos, ' ');
2325b9c547cSRui Paulo 		if (end)
2335b9c547cSRui Paulo 			len = end - pos;
2345b9c547cSRui Paulo 		else
2355b9c547cSRui Paulo 			len = os_strlen(pos);
2365b9c547cSRui Paulo 		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
2375b9c547cSRui Paulo 		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
2385b9c547cSRui Paulo 			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
2395b9c547cSRui Paulo 			os_free(data);
2405b9c547cSRui Paulo 			return NULL;
2415b9c547cSRui Paulo 		}
2425b9c547cSRui Paulo 		cfg.peer_pubkey_hash = pkhash;
2435b9c547cSRui Paulo 	}
244f05cddf9SRui Paulo 
245e28a4053SRui Paulo 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
246e28a4053SRui Paulo 	if (res < 0) {
247e28a4053SRui Paulo 		os_free(data);
2485b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
2495b9c547cSRui Paulo 			   "settings");
250e28a4053SRui Paulo 		return NULL;
251e28a4053SRui Paulo 	}
252e28a4053SRui Paulo 	if (res == 1) {
253e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
254e28a4053SRui Paulo 			   "WPS");
255e28a4053SRui Paulo 		cfg.new_ap_settings = &new_ap_settings;
256e28a4053SRui Paulo 	}
257e28a4053SRui Paulo 
2584bc52338SCy Schubert 	if (os_strstr(phase1, "multi_ap=1"))
2594bc52338SCy Schubert 		cfg.multi_ap_backhaul_sta = 1;
2604bc52338SCy Schubert 
26139beb93cSSam Leffler 	data->wps = wps_init(&cfg);
26239beb93cSSam Leffler 	if (data->wps == NULL) {
26339beb93cSSam Leffler 		os_free(data);
2645b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
26539beb93cSSam Leffler 		return NULL;
26639beb93cSSam Leffler 	}
267f05cddf9SRui Paulo 	res = eap_get_config_fragment_size(sm);
268f05cddf9SRui Paulo 	if (res > 0)
269f05cddf9SRui Paulo 		data->fragment_size = res;
270f05cddf9SRui Paulo 	else
27139beb93cSSam Leffler 		data->fragment_size = WSC_FRAGMENT_SIZE;
272f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
273f05cddf9SRui Paulo 		   (unsigned int) data->fragment_size);
27439beb93cSSam Leffler 
27539beb93cSSam Leffler 	if (registrar && cfg.pin) {
276f05cddf9SRui Paulo 		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
2773157ba21SRui Paulo 				      cfg.pin, cfg.pin_len, 0);
27839beb93cSSam Leffler 	}
27939beb93cSSam Leffler 
280e28a4053SRui Paulo 	/* Use reduced client timeout for WPS to avoid long wait */
281e28a4053SRui Paulo 	if (sm->ClientTimeout > 30)
282e28a4053SRui Paulo 		sm->ClientTimeout = 30;
283e28a4053SRui Paulo 
28439beb93cSSam Leffler 	return data;
28539beb93cSSam Leffler }
28639beb93cSSam Leffler 
28739beb93cSSam Leffler 
eap_wsc_deinit(struct eap_sm * sm,void * priv)28839beb93cSSam Leffler static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
28939beb93cSSam Leffler {
29039beb93cSSam Leffler 	struct eap_wsc_data *data = priv;
29139beb93cSSam Leffler 	wpabuf_free(data->in_buf);
29239beb93cSSam Leffler 	wpabuf_free(data->out_buf);
29339beb93cSSam Leffler 	wps_deinit(data->wps);
29439beb93cSSam Leffler 	os_free(data->wps_ctx->network_key);
29539beb93cSSam Leffler 	data->wps_ctx->network_key = NULL;
29639beb93cSSam Leffler 	os_free(data);
29739beb93cSSam Leffler }
29839beb93cSSam Leffler 
29939beb93cSSam Leffler 
eap_wsc_build_msg(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id)30039beb93cSSam Leffler static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
30139beb93cSSam Leffler 					 struct eap_method_ret *ret, u8 id)
30239beb93cSSam Leffler {
30339beb93cSSam Leffler 	struct wpabuf *resp;
30439beb93cSSam Leffler 	u8 flags;
30539beb93cSSam Leffler 	size_t send_len, plen;
30639beb93cSSam Leffler 
307*c1d255d3SCy Schubert 	ret->ignore = false;
30839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
309*c1d255d3SCy Schubert 	ret->allowNotifications = true;
31039beb93cSSam Leffler 
31139beb93cSSam Leffler 	flags = 0;
31239beb93cSSam Leffler 	send_len = wpabuf_len(data->out_buf) - data->out_used;
31339beb93cSSam Leffler 	if (2 + send_len > data->fragment_size) {
31439beb93cSSam Leffler 		send_len = data->fragment_size - 2;
31539beb93cSSam Leffler 		flags |= WSC_FLAGS_MF;
31639beb93cSSam Leffler 		if (data->out_used == 0) {
31739beb93cSSam Leffler 			flags |= WSC_FLAGS_LF;
31839beb93cSSam Leffler 			send_len -= 2;
31939beb93cSSam Leffler 		}
32039beb93cSSam Leffler 	}
32139beb93cSSam Leffler 	plen = 2 + send_len;
32239beb93cSSam Leffler 	if (flags & WSC_FLAGS_LF)
32339beb93cSSam Leffler 		plen += 2;
32439beb93cSSam Leffler 	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
32539beb93cSSam Leffler 			     EAP_CODE_RESPONSE, id);
32639beb93cSSam Leffler 	if (resp == NULL)
32739beb93cSSam Leffler 		return NULL;
32839beb93cSSam Leffler 
32939beb93cSSam Leffler 	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
33039beb93cSSam Leffler 	wpabuf_put_u8(resp, flags); /* Flags */
33139beb93cSSam Leffler 	if (flags & WSC_FLAGS_LF)
33239beb93cSSam Leffler 		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
33339beb93cSSam Leffler 
33439beb93cSSam Leffler 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
33539beb93cSSam Leffler 			send_len);
33639beb93cSSam Leffler 	data->out_used += send_len;
33739beb93cSSam Leffler 
33839beb93cSSam Leffler 	ret->methodState = METHOD_MAY_CONT;
33939beb93cSSam Leffler 	ret->decision = DECISION_FAIL;
34039beb93cSSam Leffler 
34139beb93cSSam Leffler 	if (data->out_used == wpabuf_len(data->out_buf)) {
34239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
34339beb93cSSam Leffler 			   "(message sent completely)",
34439beb93cSSam Leffler 			   (unsigned long) send_len);
34539beb93cSSam Leffler 		wpabuf_free(data->out_buf);
34639beb93cSSam Leffler 		data->out_buf = NULL;
34739beb93cSSam Leffler 		data->out_used = 0;
34839beb93cSSam Leffler 		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
34939beb93cSSam Leffler 		    data->out_op_code == WSC_NACK ||
35039beb93cSSam Leffler 		    data->out_op_code == WSC_Done) {
35139beb93cSSam Leffler 			eap_wsc_state(data, FAIL);
35239beb93cSSam Leffler 			ret->methodState = METHOD_DONE;
35339beb93cSSam Leffler 		} else
35439beb93cSSam Leffler 			eap_wsc_state(data, MESG);
35539beb93cSSam Leffler 	} else {
35639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
35739beb93cSSam Leffler 			   "(%lu more to send)", (unsigned long) send_len,
35839beb93cSSam Leffler 			   (unsigned long) wpabuf_len(data->out_buf) -
35939beb93cSSam Leffler 			   data->out_used);
36039beb93cSSam Leffler 		eap_wsc_state(data, WAIT_FRAG_ACK);
36139beb93cSSam Leffler 	}
36239beb93cSSam Leffler 
36339beb93cSSam Leffler 	return resp;
36439beb93cSSam Leffler }
36539beb93cSSam Leffler 
36639beb93cSSam Leffler 
eap_wsc_process_cont(struct eap_wsc_data * data,const u8 * buf,size_t len,u8 op_code)36739beb93cSSam Leffler static int eap_wsc_process_cont(struct eap_wsc_data *data,
36839beb93cSSam Leffler 				const u8 *buf, size_t len, u8 op_code)
36939beb93cSSam Leffler {
37039beb93cSSam Leffler 	/* Process continuation of a pending message */
37139beb93cSSam Leffler 	if (op_code != data->in_op_code) {
37239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
37339beb93cSSam Leffler 			   "fragment (expected %d)",
37439beb93cSSam Leffler 			   op_code, data->in_op_code);
37539beb93cSSam Leffler 		return -1;
37639beb93cSSam Leffler 	}
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 	if (len > wpabuf_tailroom(data->in_buf)) {
37939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
38039beb93cSSam Leffler 		eap_wsc_state(data, FAIL);
38139beb93cSSam Leffler 		return -1;
38239beb93cSSam Leffler 	}
38339beb93cSSam Leffler 
38439beb93cSSam Leffler 	wpabuf_put_data(data->in_buf, buf, len);
38539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
38639beb93cSSam Leffler 		   "for %lu bytes more", (unsigned long) len,
38739beb93cSSam Leffler 		   (unsigned long) wpabuf_tailroom(data->in_buf));
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 	return 0;
39039beb93cSSam Leffler }
39139beb93cSSam Leffler 
39239beb93cSSam Leffler 
eap_wsc_process_fragment(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id,u8 flags,u8 op_code,u16 message_length,const u8 * buf,size_t len)39339beb93cSSam Leffler static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
39439beb93cSSam Leffler 						struct eap_method_ret *ret,
39539beb93cSSam Leffler 						u8 id, u8 flags, u8 op_code,
39639beb93cSSam Leffler 						u16 message_length,
39739beb93cSSam Leffler 						const u8 *buf, size_t len)
39839beb93cSSam Leffler {
39939beb93cSSam Leffler 	/* Process a fragment that is not the last one of the message */
40039beb93cSSam Leffler 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
40139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
40239beb93cSSam Leffler 			   "fragmented packet");
403*c1d255d3SCy Schubert 		ret->ignore = true;
40439beb93cSSam Leffler 		return NULL;
40539beb93cSSam Leffler 	}
40639beb93cSSam Leffler 
40739beb93cSSam Leffler 	if (data->in_buf == NULL) {
40839beb93cSSam Leffler 		/* First fragment of the message */
40939beb93cSSam Leffler 		data->in_buf = wpabuf_alloc(message_length);
41039beb93cSSam Leffler 		if (data->in_buf == NULL) {
41139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
41239beb93cSSam Leffler 				   "message");
413*c1d255d3SCy Schubert 			ret->ignore = true;
41439beb93cSSam Leffler 			return NULL;
41539beb93cSSam Leffler 		}
41639beb93cSSam Leffler 		data->in_op_code = op_code;
41739beb93cSSam Leffler 		wpabuf_put_data(data->in_buf, buf, len);
41839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
41939beb93cSSam Leffler 			   "fragment, waiting for %lu bytes more",
42039beb93cSSam Leffler 			   (unsigned long) len,
42139beb93cSSam Leffler 			   (unsigned long) wpabuf_tailroom(data->in_buf));
42239beb93cSSam Leffler 	}
42339beb93cSSam Leffler 
42439beb93cSSam Leffler 	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
42539beb93cSSam Leffler }
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 
eap_wsc_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)42839beb93cSSam Leffler static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
42939beb93cSSam Leffler 				       struct eap_method_ret *ret,
43039beb93cSSam Leffler 				       const struct wpabuf *reqData)
43139beb93cSSam Leffler {
43239beb93cSSam Leffler 	struct eap_wsc_data *data = priv;
43339beb93cSSam Leffler 	const u8 *start, *pos, *end;
43439beb93cSSam Leffler 	size_t len;
43539beb93cSSam Leffler 	u8 op_code, flags, id;
43639beb93cSSam Leffler 	u16 message_length = 0;
43739beb93cSSam Leffler 	enum wps_process_res res;
43839beb93cSSam Leffler 	struct wpabuf tmpbuf;
439e28a4053SRui Paulo 	struct wpabuf *r;
44039beb93cSSam Leffler 
44139beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
44239beb93cSSam Leffler 			       &len);
44339beb93cSSam Leffler 	if (pos == NULL || len < 2) {
444*c1d255d3SCy Schubert 		ret->ignore = true;
44539beb93cSSam Leffler 		return NULL;
44639beb93cSSam Leffler 	}
44739beb93cSSam Leffler 
44839beb93cSSam Leffler 	id = eap_get_id(reqData);
44939beb93cSSam Leffler 
45039beb93cSSam Leffler 	start = pos;
45139beb93cSSam Leffler 	end = start + len;
45239beb93cSSam Leffler 
45339beb93cSSam Leffler 	op_code = *pos++;
45439beb93cSSam Leffler 	flags = *pos++;
45539beb93cSSam Leffler 	if (flags & WSC_FLAGS_LF) {
45639beb93cSSam Leffler 		if (end - pos < 2) {
45739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
458*c1d255d3SCy Schubert 			ret->ignore = true;
45939beb93cSSam Leffler 			return NULL;
46039beb93cSSam Leffler 		}
46139beb93cSSam Leffler 		message_length = WPA_GET_BE16(pos);
46239beb93cSSam Leffler 		pos += 2;
46339beb93cSSam Leffler 
4645b9c547cSRui Paulo 		if (message_length < end - pos || message_length > 50000) {
46539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
46639beb93cSSam Leffler 				   "Length");
467*c1d255d3SCy Schubert 			ret->ignore = true;
46839beb93cSSam Leffler 			return NULL;
46939beb93cSSam Leffler 		}
47039beb93cSSam Leffler 	}
47139beb93cSSam Leffler 
47239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
47339beb93cSSam Leffler 		   "Flags 0x%x Message Length %d",
47439beb93cSSam Leffler 		   op_code, flags, message_length);
47539beb93cSSam Leffler 
47639beb93cSSam Leffler 	if (data->state == WAIT_FRAG_ACK) {
47739beb93cSSam Leffler 		if (op_code != WSC_FRAG_ACK) {
47839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
47939beb93cSSam Leffler 				   "in WAIT_FRAG_ACK state", op_code);
480*c1d255d3SCy Schubert 			ret->ignore = true;
48139beb93cSSam Leffler 			return NULL;
48239beb93cSSam Leffler 		}
48339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
48439beb93cSSam Leffler 		eap_wsc_state(data, MESG);
48539beb93cSSam Leffler 		return eap_wsc_build_msg(data, ret, id);
48639beb93cSSam Leffler 	}
48739beb93cSSam Leffler 
48839beb93cSSam Leffler 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
48939beb93cSSam Leffler 	    op_code != WSC_Done && op_code != WSC_Start) {
49039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
49139beb93cSSam Leffler 			   op_code);
492*c1d255d3SCy Schubert 		ret->ignore = true;
49339beb93cSSam Leffler 		return NULL;
49439beb93cSSam Leffler 	}
49539beb93cSSam Leffler 
49639beb93cSSam Leffler 	if (data->state == WAIT_START) {
49739beb93cSSam Leffler 		if (op_code != WSC_Start) {
49839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
49939beb93cSSam Leffler 				   "in WAIT_START state", op_code);
500*c1d255d3SCy Schubert 			ret->ignore = true;
50139beb93cSSam Leffler 			return NULL;
50239beb93cSSam Leffler 		}
50339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
50439beb93cSSam Leffler 		eap_wsc_state(data, MESG);
50539beb93cSSam Leffler 		/* Start message has empty payload, skip processing */
50639beb93cSSam Leffler 		goto send_msg;
50739beb93cSSam Leffler 	} else if (op_code == WSC_Start) {
50839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
50939beb93cSSam Leffler 			   op_code);
510*c1d255d3SCy Schubert 		ret->ignore = true;
51139beb93cSSam Leffler 		return NULL;
51239beb93cSSam Leffler 	}
51339beb93cSSam Leffler 
51439beb93cSSam Leffler 	if (data->in_buf &&
51539beb93cSSam Leffler 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
516*c1d255d3SCy Schubert 		ret->ignore = true;
51739beb93cSSam Leffler 		return NULL;
51839beb93cSSam Leffler 	}
51939beb93cSSam Leffler 
52039beb93cSSam Leffler 	if (flags & WSC_FLAGS_MF) {
52139beb93cSSam Leffler 		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
52239beb93cSSam Leffler 						message_length, pos,
52339beb93cSSam Leffler 						end - pos);
52439beb93cSSam Leffler 	}
52539beb93cSSam Leffler 
52639beb93cSSam Leffler 	if (data->in_buf == NULL) {
52739beb93cSSam Leffler 		/* Wrap unfragmented messages as wpabuf without extra copy */
52839beb93cSSam Leffler 		wpabuf_set(&tmpbuf, pos, end - pos);
52939beb93cSSam Leffler 		data->in_buf = &tmpbuf;
53039beb93cSSam Leffler 	}
53139beb93cSSam Leffler 
53239beb93cSSam Leffler 	res = wps_process_msg(data->wps, op_code, data->in_buf);
53339beb93cSSam Leffler 	switch (res) {
53439beb93cSSam Leffler 	case WPS_DONE:
53539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
53639beb93cSSam Leffler 			   "successfully - wait for EAP failure");
53739beb93cSSam Leffler 		eap_wsc_state(data, FAIL);
53839beb93cSSam Leffler 		break;
53939beb93cSSam Leffler 	case WPS_CONTINUE:
54039beb93cSSam Leffler 		eap_wsc_state(data, MESG);
54139beb93cSSam Leffler 		break;
54239beb93cSSam Leffler 	case WPS_FAILURE:
54339beb93cSSam Leffler 	case WPS_PENDING:
54439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
54539beb93cSSam Leffler 		eap_wsc_state(data, FAIL);
54639beb93cSSam Leffler 		break;
54739beb93cSSam Leffler 	}
54839beb93cSSam Leffler 
54939beb93cSSam Leffler 	if (data->in_buf != &tmpbuf)
55039beb93cSSam Leffler 		wpabuf_free(data->in_buf);
55139beb93cSSam Leffler 	data->in_buf = NULL;
55239beb93cSSam Leffler 
55339beb93cSSam Leffler send_msg:
55439beb93cSSam Leffler 	if (data->out_buf == NULL) {
55539beb93cSSam Leffler 		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
55639beb93cSSam Leffler 		if (data->out_buf == NULL) {
55739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
55839beb93cSSam Leffler 				   "message from WPS");
559325151a3SRui Paulo 			eap_wsc_state(data, FAIL);
560325151a3SRui Paulo 			ret->methodState = METHOD_DONE;
561325151a3SRui Paulo 			ret->decision = DECISION_FAIL;
56239beb93cSSam Leffler 			return NULL;
56339beb93cSSam Leffler 		}
56439beb93cSSam Leffler 		data->out_used = 0;
56539beb93cSSam Leffler 	}
56639beb93cSSam Leffler 
56739beb93cSSam Leffler 	eap_wsc_state(data, MESG);
568e28a4053SRui Paulo 	r = eap_wsc_build_msg(data, ret, id);
569e28a4053SRui Paulo 	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
570e28a4053SRui Paulo 		/* Use reduced client timeout for WPS to avoid long wait */
571e28a4053SRui Paulo 		if (sm->ClientTimeout > 2)
572e28a4053SRui Paulo 			sm->ClientTimeout = 2;
573e28a4053SRui Paulo 	}
574e28a4053SRui Paulo 	return r;
57539beb93cSSam Leffler }
57639beb93cSSam Leffler 
57739beb93cSSam Leffler 
eap_peer_wsc_register(void)57839beb93cSSam Leffler int eap_peer_wsc_register(void)
57939beb93cSSam Leffler {
58039beb93cSSam Leffler 	struct eap_method *eap;
58139beb93cSSam Leffler 
58239beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
58339beb93cSSam Leffler 				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
58439beb93cSSam Leffler 				    "WSC");
58539beb93cSSam Leffler 	if (eap == NULL)
58639beb93cSSam Leffler 		return -1;
58739beb93cSSam Leffler 
58839beb93cSSam Leffler 	eap->init = eap_wsc_init;
58939beb93cSSam Leffler 	eap->deinit = eap_wsc_deinit;
59039beb93cSSam Leffler 	eap->process = eap_wsc_process;
59139beb93cSSam Leffler 
592780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
59339beb93cSSam Leffler }
594