xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_peer/eap_wsc.c (revision 3ff40c12445a6f5918b9c6e9c7031bc0cb8786d1)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP-WSC peer for Wi-Fi Protected Setup
3*3ff40c12SJohn Marino  * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
5*3ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
6*3ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  */
86d49e1aeSJan Lentfer 
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer 
116d49e1aeSJan Lentfer #include "common.h"
126d49e1aeSJan Lentfer #include "uuid.h"
136d49e1aeSJan Lentfer #include "eap_i.h"
146d49e1aeSJan Lentfer #include "eap_common/eap_wsc_common.h"
156d49e1aeSJan Lentfer #include "wps/wps.h"
166d49e1aeSJan Lentfer #include "wps/wps_defs.h"
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer 
196d49e1aeSJan Lentfer struct eap_wsc_data {
206d49e1aeSJan Lentfer 	enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
216d49e1aeSJan Lentfer 	int registrar;
226d49e1aeSJan Lentfer 	struct wpabuf *in_buf;
236d49e1aeSJan Lentfer 	struct wpabuf *out_buf;
246d49e1aeSJan Lentfer 	enum wsc_op_code in_op_code, out_op_code;
256d49e1aeSJan Lentfer 	size_t out_used;
266d49e1aeSJan Lentfer 	size_t fragment_size;
276d49e1aeSJan Lentfer 	struct wps_data *wps;
286d49e1aeSJan Lentfer 	struct wps_context *wps_ctx;
296d49e1aeSJan Lentfer };
306d49e1aeSJan Lentfer 
316d49e1aeSJan Lentfer 
326d49e1aeSJan Lentfer static const char * eap_wsc_state_txt(int state)
336d49e1aeSJan Lentfer {
346d49e1aeSJan Lentfer 	switch (state) {
356d49e1aeSJan Lentfer 	case WAIT_START:
366d49e1aeSJan Lentfer 		return "WAIT_START";
376d49e1aeSJan Lentfer 	case MESG:
386d49e1aeSJan Lentfer 		return "MESG";
396d49e1aeSJan Lentfer 	case FRAG_ACK:
406d49e1aeSJan Lentfer 		return "FRAG_ACK";
416d49e1aeSJan Lentfer 	case WAIT_FRAG_ACK:
426d49e1aeSJan Lentfer 		return "WAIT_FRAG_ACK";
436d49e1aeSJan Lentfer 	case DONE:
446d49e1aeSJan Lentfer 		return "DONE";
456d49e1aeSJan Lentfer 	case FAIL:
466d49e1aeSJan Lentfer 		return "FAIL";
476d49e1aeSJan Lentfer 	default:
486d49e1aeSJan Lentfer 		return "?";
496d49e1aeSJan Lentfer 	}
506d49e1aeSJan Lentfer }
516d49e1aeSJan Lentfer 
526d49e1aeSJan Lentfer 
536d49e1aeSJan Lentfer static void eap_wsc_state(struct eap_wsc_data *data, int state)
546d49e1aeSJan Lentfer {
556d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
566d49e1aeSJan Lentfer 		   eap_wsc_state_txt(data->state),
576d49e1aeSJan Lentfer 		   eap_wsc_state_txt(state));
586d49e1aeSJan Lentfer 	data->state = state;
596d49e1aeSJan Lentfer }
606d49e1aeSJan Lentfer 
616d49e1aeSJan Lentfer 
62*3ff40c12SJohn Marino static int eap_wsc_new_ap_settings(struct wps_credential *cred,
63*3ff40c12SJohn Marino 				   const char *params)
64*3ff40c12SJohn Marino {
65*3ff40c12SJohn Marino 	const char *pos, *end;
66*3ff40c12SJohn Marino 	size_t len;
67*3ff40c12SJohn Marino 
68*3ff40c12SJohn Marino 	os_memset(cred, 0, sizeof(*cred));
69*3ff40c12SJohn Marino 
70*3ff40c12SJohn Marino 	pos = os_strstr(params, "new_ssid=");
71*3ff40c12SJohn Marino 	if (pos == NULL)
72*3ff40c12SJohn Marino 		return 0;
73*3ff40c12SJohn Marino 	pos += 9;
74*3ff40c12SJohn Marino 	end = os_strchr(pos, ' ');
75*3ff40c12SJohn Marino 	if (end == NULL)
76*3ff40c12SJohn Marino 		len = os_strlen(pos);
77*3ff40c12SJohn Marino 	else
78*3ff40c12SJohn Marino 		len = end - pos;
79*3ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
80*3ff40c12SJohn Marino 	    hexstr2bin(pos, cred->ssid, len / 2)) {
81*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
82*3ff40c12SJohn Marino 		return -1;
83*3ff40c12SJohn Marino 	}
84*3ff40c12SJohn Marino 	cred->ssid_len = len / 2;
85*3ff40c12SJohn Marino 
86*3ff40c12SJohn Marino 	pos = os_strstr(params, "new_auth=");
87*3ff40c12SJohn Marino 	if (pos == NULL) {
88*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
89*3ff40c12SJohn Marino 		return -1;
90*3ff40c12SJohn Marino 	}
91*3ff40c12SJohn Marino 	if (os_strncmp(pos + 9, "OPEN", 4) == 0)
92*3ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_OPEN;
93*3ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
94*3ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_WPAPSK;
95*3ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
96*3ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_WPA2PSK;
97*3ff40c12SJohn Marino 	else {
98*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
99*3ff40c12SJohn Marino 		return -1;
100*3ff40c12SJohn Marino 	}
101*3ff40c12SJohn Marino 
102*3ff40c12SJohn Marino 	pos = os_strstr(params, "new_encr=");
103*3ff40c12SJohn Marino 	if (pos == NULL) {
104*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
105*3ff40c12SJohn Marino 		return -1;
106*3ff40c12SJohn Marino 	}
107*3ff40c12SJohn Marino 	if (os_strncmp(pos + 9, "NONE", 4) == 0)
108*3ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_NONE;
109*3ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WEP", 3) == 0)
110*3ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_WEP;
111*3ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
112*3ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_TKIP;
113*3ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
114*3ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_AES;
115*3ff40c12SJohn Marino 	else {
116*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
117*3ff40c12SJohn Marino 		return -1;
118*3ff40c12SJohn Marino 	}
119*3ff40c12SJohn Marino 
120*3ff40c12SJohn Marino 	pos = os_strstr(params, "new_key=");
121*3ff40c12SJohn Marino 	if (pos == NULL)
122*3ff40c12SJohn Marino 		return 0;
123*3ff40c12SJohn Marino 	pos += 8;
124*3ff40c12SJohn Marino 	end = os_strchr(pos, ' ');
125*3ff40c12SJohn Marino 	if (end == NULL)
126*3ff40c12SJohn Marino 		len = os_strlen(pos);
127*3ff40c12SJohn Marino 	else
128*3ff40c12SJohn Marino 		len = end - pos;
129*3ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred->key) ||
130*3ff40c12SJohn Marino 	    hexstr2bin(pos, cred->key, len / 2)) {
131*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
132*3ff40c12SJohn Marino 		return -1;
133*3ff40c12SJohn Marino 	}
134*3ff40c12SJohn Marino 	cred->key_len = len / 2;
135*3ff40c12SJohn Marino 
136*3ff40c12SJohn Marino 	return 1;
137*3ff40c12SJohn Marino }
138*3ff40c12SJohn Marino 
139*3ff40c12SJohn Marino 
1406d49e1aeSJan Lentfer static void * eap_wsc_init(struct eap_sm *sm)
1416d49e1aeSJan Lentfer {
1426d49e1aeSJan Lentfer 	struct eap_wsc_data *data;
1436d49e1aeSJan Lentfer 	const u8 *identity;
1446d49e1aeSJan Lentfer 	size_t identity_len;
1456d49e1aeSJan Lentfer 	int registrar;
1466d49e1aeSJan Lentfer 	struct wps_config cfg;
147*3ff40c12SJohn Marino 	const char *pos, *end;
1486d49e1aeSJan Lentfer 	const char *phase1;
1496d49e1aeSJan Lentfer 	struct wps_context *wps;
150*3ff40c12SJohn Marino 	struct wps_credential new_ap_settings;
151*3ff40c12SJohn Marino 	int res;
152*3ff40c12SJohn Marino 	int nfc = 0;
153*3ff40c12SJohn Marino 	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
1546d49e1aeSJan Lentfer 
1556d49e1aeSJan Lentfer 	wps = sm->wps;
1566d49e1aeSJan Lentfer 	if (wps == NULL) {
1576d49e1aeSJan Lentfer 		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
1586d49e1aeSJan Lentfer 		return NULL;
1596d49e1aeSJan Lentfer 	}
1606d49e1aeSJan Lentfer 
1616d49e1aeSJan Lentfer 	identity = eap_get_config_identity(sm, &identity_len);
1626d49e1aeSJan Lentfer 
1636d49e1aeSJan Lentfer 	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
1646d49e1aeSJan Lentfer 	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
1656d49e1aeSJan Lentfer 		registrar = 1; /* Supplicant is Registrar */
1666d49e1aeSJan Lentfer 	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
1676d49e1aeSJan Lentfer 	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
1686d49e1aeSJan Lentfer 		registrar = 0; /* Supplicant is Enrollee */
1696d49e1aeSJan Lentfer 	else {
1706d49e1aeSJan Lentfer 		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
1716d49e1aeSJan Lentfer 				  identity, identity_len);
1726d49e1aeSJan Lentfer 		return NULL;
1736d49e1aeSJan Lentfer 	}
1746d49e1aeSJan Lentfer 
1756d49e1aeSJan Lentfer 	data = os_zalloc(sizeof(*data));
1766d49e1aeSJan Lentfer 	if (data == NULL)
1776d49e1aeSJan Lentfer 		return NULL;
1786d49e1aeSJan Lentfer 	data->state = registrar ? MESG : WAIT_START;
1796d49e1aeSJan Lentfer 	data->registrar = registrar;
1806d49e1aeSJan Lentfer 	data->wps_ctx = wps;
1816d49e1aeSJan Lentfer 
1826d49e1aeSJan Lentfer 	os_memset(&cfg, 0, sizeof(cfg));
1836d49e1aeSJan Lentfer 	cfg.wps = wps;
1846d49e1aeSJan Lentfer 	cfg.registrar = registrar;
1856d49e1aeSJan Lentfer 
1866d49e1aeSJan Lentfer 	phase1 = eap_get_config_phase1(sm);
1876d49e1aeSJan Lentfer 	if (phase1 == NULL) {
1886d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
1896d49e1aeSJan Lentfer 			   "set");
1906d49e1aeSJan Lentfer 		os_free(data);
1916d49e1aeSJan Lentfer 		return NULL;
1926d49e1aeSJan Lentfer 	}
1936d49e1aeSJan Lentfer 
1946d49e1aeSJan Lentfer 	pos = os_strstr(phase1, "pin=");
1956d49e1aeSJan Lentfer 	if (pos) {
1966d49e1aeSJan Lentfer 		pos += 4;
1976d49e1aeSJan Lentfer 		cfg.pin = (const u8 *) pos;
1986d49e1aeSJan Lentfer 		while (*pos != '\0' && *pos != ' ')
1996d49e1aeSJan Lentfer 			pos++;
2006d49e1aeSJan Lentfer 		cfg.pin_len = pos - (const char *) cfg.pin;
201*3ff40c12SJohn Marino 		if (cfg.pin_len == 6 &&
202*3ff40c12SJohn Marino 		    os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
203*3ff40c12SJohn Marino 			cfg.pin = NULL;
204*3ff40c12SJohn Marino 			cfg.pin_len = 0;
205*3ff40c12SJohn Marino 			nfc = 1;
206*3ff40c12SJohn Marino 		}
2076d49e1aeSJan Lentfer 	} else {
2086d49e1aeSJan Lentfer 		pos = os_strstr(phase1, "pbc=1");
2096d49e1aeSJan Lentfer 		if (pos)
2106d49e1aeSJan Lentfer 			cfg.pbc = 1;
2116d49e1aeSJan Lentfer 	}
2126d49e1aeSJan Lentfer 
213*3ff40c12SJohn Marino 	pos = os_strstr(phase1, "dev_pw_id=");
214*3ff40c12SJohn Marino 	if (pos) {
215*3ff40c12SJohn Marino 		u16 id = atoi(pos + 10);
216*3ff40c12SJohn Marino 		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
217*3ff40c12SJohn Marino 			nfc = 1;
218*3ff40c12SJohn Marino 		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
219*3ff40c12SJohn Marino 			cfg.dev_pw_id = id;
220*3ff40c12SJohn Marino 	}
221*3ff40c12SJohn Marino 
222*3ff40c12SJohn Marino 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
2236d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
2246d49e1aeSJan Lentfer 			   "configuration data");
2256d49e1aeSJan Lentfer 		os_free(data);
2266d49e1aeSJan Lentfer 		return NULL;
2276d49e1aeSJan Lentfer 	}
2286d49e1aeSJan Lentfer 
229*3ff40c12SJohn Marino 	pos = os_strstr(phase1, " pkhash=");
230*3ff40c12SJohn Marino 	if (pos) {
231*3ff40c12SJohn Marino 		size_t len;
232*3ff40c12SJohn Marino 		pos += 8;
233*3ff40c12SJohn Marino 		end = os_strchr(pos, ' ');
234*3ff40c12SJohn Marino 		if (end)
235*3ff40c12SJohn Marino 			len = end - pos;
236*3ff40c12SJohn Marino 		else
237*3ff40c12SJohn Marino 			len = os_strlen(pos);
238*3ff40c12SJohn Marino 		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
239*3ff40c12SJohn Marino 		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
240*3ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
2416d49e1aeSJan Lentfer 			os_free(data);
2426d49e1aeSJan Lentfer 			return NULL;
2436d49e1aeSJan Lentfer 		}
244*3ff40c12SJohn Marino 		cfg.peer_pubkey_hash = pkhash;
245*3ff40c12SJohn Marino 	}
246*3ff40c12SJohn Marino 
247*3ff40c12SJohn Marino 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
248*3ff40c12SJohn Marino 	if (res < 0) {
249*3ff40c12SJohn Marino 		os_free(data);
250*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
251*3ff40c12SJohn Marino 			   "settings");
252*3ff40c12SJohn Marino 		return NULL;
253*3ff40c12SJohn Marino 	}
254*3ff40c12SJohn Marino 	if (res == 1) {
255*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
256*3ff40c12SJohn Marino 			   "WPS");
257*3ff40c12SJohn Marino 		cfg.new_ap_settings = &new_ap_settings;
258*3ff40c12SJohn Marino 	}
259*3ff40c12SJohn Marino 
260*3ff40c12SJohn Marino 	data->wps = wps_init(&cfg);
261*3ff40c12SJohn Marino 	if (data->wps == NULL) {
262*3ff40c12SJohn Marino 		os_free(data);
263*3ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
264*3ff40c12SJohn Marino 		return NULL;
265*3ff40c12SJohn Marino 	}
266*3ff40c12SJohn Marino 	res = eap_get_config_fragment_size(sm);
267*3ff40c12SJohn Marino 	if (res > 0)
268*3ff40c12SJohn Marino 		data->fragment_size = res;
269*3ff40c12SJohn Marino 	else
2706d49e1aeSJan Lentfer 		data->fragment_size = WSC_FRAGMENT_SIZE;
271*3ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
272*3ff40c12SJohn Marino 		   (unsigned int) data->fragment_size);
2736d49e1aeSJan Lentfer 
2746d49e1aeSJan Lentfer 	if (registrar && cfg.pin) {
275*3ff40c12SJohn Marino 		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
2766d49e1aeSJan Lentfer 				      cfg.pin, cfg.pin_len, 0);
2776d49e1aeSJan Lentfer 	}
2786d49e1aeSJan Lentfer 
279*3ff40c12SJohn Marino 	/* Use reduced client timeout for WPS to avoid long wait */
280*3ff40c12SJohn Marino 	if (sm->ClientTimeout > 30)
281*3ff40c12SJohn Marino 		sm->ClientTimeout = 30;
282*3ff40c12SJohn Marino 
2836d49e1aeSJan Lentfer 	return data;
2846d49e1aeSJan Lentfer }
2856d49e1aeSJan Lentfer 
2866d49e1aeSJan Lentfer 
2876d49e1aeSJan Lentfer static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
2886d49e1aeSJan Lentfer {
2896d49e1aeSJan Lentfer 	struct eap_wsc_data *data = priv;
2906d49e1aeSJan Lentfer 	wpabuf_free(data->in_buf);
2916d49e1aeSJan Lentfer 	wpabuf_free(data->out_buf);
2926d49e1aeSJan Lentfer 	wps_deinit(data->wps);
2936d49e1aeSJan Lentfer 	os_free(data->wps_ctx->network_key);
2946d49e1aeSJan Lentfer 	data->wps_ctx->network_key = NULL;
2956d49e1aeSJan Lentfer 	os_free(data);
2966d49e1aeSJan Lentfer }
2976d49e1aeSJan Lentfer 
2986d49e1aeSJan Lentfer 
2996d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
3006d49e1aeSJan Lentfer 					 struct eap_method_ret *ret, u8 id)
3016d49e1aeSJan Lentfer {
3026d49e1aeSJan Lentfer 	struct wpabuf *resp;
3036d49e1aeSJan Lentfer 	u8 flags;
3046d49e1aeSJan Lentfer 	size_t send_len, plen;
3056d49e1aeSJan Lentfer 
3066d49e1aeSJan Lentfer 	ret->ignore = FALSE;
3076d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
3086d49e1aeSJan Lentfer 	ret->allowNotifications = TRUE;
3096d49e1aeSJan Lentfer 
3106d49e1aeSJan Lentfer 	flags = 0;
3116d49e1aeSJan Lentfer 	send_len = wpabuf_len(data->out_buf) - data->out_used;
3126d49e1aeSJan Lentfer 	if (2 + send_len > data->fragment_size) {
3136d49e1aeSJan Lentfer 		send_len = data->fragment_size - 2;
3146d49e1aeSJan Lentfer 		flags |= WSC_FLAGS_MF;
3156d49e1aeSJan Lentfer 		if (data->out_used == 0) {
3166d49e1aeSJan Lentfer 			flags |= WSC_FLAGS_LF;
3176d49e1aeSJan Lentfer 			send_len -= 2;
3186d49e1aeSJan Lentfer 		}
3196d49e1aeSJan Lentfer 	}
3206d49e1aeSJan Lentfer 	plen = 2 + send_len;
3216d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF)
3226d49e1aeSJan Lentfer 		plen += 2;
3236d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
3246d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
3256d49e1aeSJan Lentfer 	if (resp == NULL)
3266d49e1aeSJan Lentfer 		return NULL;
3276d49e1aeSJan Lentfer 
3286d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
3296d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, flags); /* Flags */
3306d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF)
3316d49e1aeSJan Lentfer 		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
3326d49e1aeSJan Lentfer 
3336d49e1aeSJan Lentfer 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
3346d49e1aeSJan Lentfer 			send_len);
3356d49e1aeSJan Lentfer 	data->out_used += send_len;
3366d49e1aeSJan Lentfer 
3376d49e1aeSJan Lentfer 	ret->methodState = METHOD_MAY_CONT;
3386d49e1aeSJan Lentfer 	ret->decision = DECISION_FAIL;
3396d49e1aeSJan Lentfer 
3406d49e1aeSJan Lentfer 	if (data->out_used == wpabuf_len(data->out_buf)) {
3416d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
3426d49e1aeSJan Lentfer 			   "(message sent completely)",
3436d49e1aeSJan Lentfer 			   (unsigned long) send_len);
3446d49e1aeSJan Lentfer 		wpabuf_free(data->out_buf);
3456d49e1aeSJan Lentfer 		data->out_buf = NULL;
3466d49e1aeSJan Lentfer 		data->out_used = 0;
3476d49e1aeSJan Lentfer 		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
3486d49e1aeSJan Lentfer 		    data->out_op_code == WSC_NACK ||
3496d49e1aeSJan Lentfer 		    data->out_op_code == WSC_Done) {
3506d49e1aeSJan Lentfer 			eap_wsc_state(data, FAIL);
3516d49e1aeSJan Lentfer 			ret->methodState = METHOD_DONE;
3526d49e1aeSJan Lentfer 		} else
3536d49e1aeSJan Lentfer 			eap_wsc_state(data, MESG);
3546d49e1aeSJan Lentfer 	} else {
3556d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
3566d49e1aeSJan Lentfer 			   "(%lu more to send)", (unsigned long) send_len,
3576d49e1aeSJan Lentfer 			   (unsigned long) wpabuf_len(data->out_buf) -
3586d49e1aeSJan Lentfer 			   data->out_used);
3596d49e1aeSJan Lentfer 		eap_wsc_state(data, WAIT_FRAG_ACK);
3606d49e1aeSJan Lentfer 	}
3616d49e1aeSJan Lentfer 
3626d49e1aeSJan Lentfer 	return resp;
3636d49e1aeSJan Lentfer }
3646d49e1aeSJan Lentfer 
3656d49e1aeSJan Lentfer 
3666d49e1aeSJan Lentfer static int eap_wsc_process_cont(struct eap_wsc_data *data,
3676d49e1aeSJan Lentfer 				const u8 *buf, size_t len, u8 op_code)
3686d49e1aeSJan Lentfer {
3696d49e1aeSJan Lentfer 	/* Process continuation of a pending message */
3706d49e1aeSJan Lentfer 	if (op_code != data->in_op_code) {
3716d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
3726d49e1aeSJan Lentfer 			   "fragment (expected %d)",
3736d49e1aeSJan Lentfer 			   op_code, data->in_op_code);
3746d49e1aeSJan Lentfer 		return -1;
3756d49e1aeSJan Lentfer 	}
3766d49e1aeSJan Lentfer 
3776d49e1aeSJan Lentfer 	if (len > wpabuf_tailroom(data->in_buf)) {
3786d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
3796d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
3806d49e1aeSJan Lentfer 		return -1;
3816d49e1aeSJan Lentfer 	}
3826d49e1aeSJan Lentfer 
3836d49e1aeSJan Lentfer 	wpabuf_put_data(data->in_buf, buf, len);
3846d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
3856d49e1aeSJan Lentfer 		   "for %lu bytes more", (unsigned long) len,
3866d49e1aeSJan Lentfer 		   (unsigned long) wpabuf_tailroom(data->in_buf));
3876d49e1aeSJan Lentfer 
3886d49e1aeSJan Lentfer 	return 0;
3896d49e1aeSJan Lentfer }
3906d49e1aeSJan Lentfer 
3916d49e1aeSJan Lentfer 
3926d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
3936d49e1aeSJan Lentfer 						struct eap_method_ret *ret,
3946d49e1aeSJan Lentfer 						u8 id, u8 flags, u8 op_code,
3956d49e1aeSJan Lentfer 						u16 message_length,
3966d49e1aeSJan Lentfer 						const u8 *buf, size_t len)
3976d49e1aeSJan Lentfer {
3986d49e1aeSJan Lentfer 	/* Process a fragment that is not the last one of the message */
3996d49e1aeSJan Lentfer 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
4006d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
4016d49e1aeSJan Lentfer 			   "fragmented packet");
4026d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4036d49e1aeSJan Lentfer 		return NULL;
4046d49e1aeSJan Lentfer 	}
4056d49e1aeSJan Lentfer 
4066d49e1aeSJan Lentfer 	if (data->in_buf == NULL) {
4076d49e1aeSJan Lentfer 		/* First fragment of the message */
4086d49e1aeSJan Lentfer 		data->in_buf = wpabuf_alloc(message_length);
4096d49e1aeSJan Lentfer 		if (data->in_buf == NULL) {
4106d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
4116d49e1aeSJan Lentfer 				   "message");
4126d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4136d49e1aeSJan Lentfer 			return NULL;
4146d49e1aeSJan Lentfer 		}
4156d49e1aeSJan Lentfer 		data->in_op_code = op_code;
4166d49e1aeSJan Lentfer 		wpabuf_put_data(data->in_buf, buf, len);
4176d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
4186d49e1aeSJan Lentfer 			   "fragment, waiting for %lu bytes more",
4196d49e1aeSJan Lentfer 			   (unsigned long) len,
4206d49e1aeSJan Lentfer 			   (unsigned long) wpabuf_tailroom(data->in_buf));
4216d49e1aeSJan Lentfer 	}
4226d49e1aeSJan Lentfer 
4236d49e1aeSJan Lentfer 	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
4246d49e1aeSJan Lentfer }
4256d49e1aeSJan Lentfer 
4266d49e1aeSJan Lentfer 
4276d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
4286d49e1aeSJan Lentfer 				       struct eap_method_ret *ret,
4296d49e1aeSJan Lentfer 				       const struct wpabuf *reqData)
4306d49e1aeSJan Lentfer {
4316d49e1aeSJan Lentfer 	struct eap_wsc_data *data = priv;
4326d49e1aeSJan Lentfer 	const u8 *start, *pos, *end;
4336d49e1aeSJan Lentfer 	size_t len;
4346d49e1aeSJan Lentfer 	u8 op_code, flags, id;
4356d49e1aeSJan Lentfer 	u16 message_length = 0;
4366d49e1aeSJan Lentfer 	enum wps_process_res res;
4376d49e1aeSJan Lentfer 	struct wpabuf tmpbuf;
438*3ff40c12SJohn Marino 	struct wpabuf *r;
4396d49e1aeSJan Lentfer 
4406d49e1aeSJan Lentfer 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
4416d49e1aeSJan Lentfer 			       &len);
4426d49e1aeSJan Lentfer 	if (pos == NULL || len < 2) {
4436d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4446d49e1aeSJan Lentfer 		return NULL;
4456d49e1aeSJan Lentfer 	}
4466d49e1aeSJan Lentfer 
4476d49e1aeSJan Lentfer 	id = eap_get_id(reqData);
4486d49e1aeSJan Lentfer 
4496d49e1aeSJan Lentfer 	start = pos;
4506d49e1aeSJan Lentfer 	end = start + len;
4516d49e1aeSJan Lentfer 
4526d49e1aeSJan Lentfer 	op_code = *pos++;
4536d49e1aeSJan Lentfer 	flags = *pos++;
4546d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF) {
4556d49e1aeSJan Lentfer 		if (end - pos < 2) {
4566d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
4576d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4586d49e1aeSJan Lentfer 			return NULL;
4596d49e1aeSJan Lentfer 		}
4606d49e1aeSJan Lentfer 		message_length = WPA_GET_BE16(pos);
4616d49e1aeSJan Lentfer 		pos += 2;
4626d49e1aeSJan Lentfer 
4636d49e1aeSJan Lentfer 		if (message_length < end - pos) {
4646d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
4656d49e1aeSJan Lentfer 				   "Length");
4666d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4676d49e1aeSJan Lentfer 			return NULL;
4686d49e1aeSJan Lentfer 		}
4696d49e1aeSJan Lentfer 	}
4706d49e1aeSJan Lentfer 
4716d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
4726d49e1aeSJan Lentfer 		   "Flags 0x%x Message Length %d",
4736d49e1aeSJan Lentfer 		   op_code, flags, message_length);
4746d49e1aeSJan Lentfer 
4756d49e1aeSJan Lentfer 	if (data->state == WAIT_FRAG_ACK) {
4766d49e1aeSJan Lentfer 		if (op_code != WSC_FRAG_ACK) {
4776d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
4786d49e1aeSJan Lentfer 				   "in WAIT_FRAG_ACK state", op_code);
4796d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4806d49e1aeSJan Lentfer 			return NULL;
4816d49e1aeSJan Lentfer 		}
4826d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
4836d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
4846d49e1aeSJan Lentfer 		return eap_wsc_build_msg(data, ret, id);
4856d49e1aeSJan Lentfer 	}
4866d49e1aeSJan Lentfer 
4876d49e1aeSJan Lentfer 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
4886d49e1aeSJan Lentfer 	    op_code != WSC_Done && op_code != WSC_Start) {
4896d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
4906d49e1aeSJan Lentfer 			   op_code);
4916d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4926d49e1aeSJan Lentfer 		return NULL;
4936d49e1aeSJan Lentfer 	}
4946d49e1aeSJan Lentfer 
4956d49e1aeSJan Lentfer 	if (data->state == WAIT_START) {
4966d49e1aeSJan Lentfer 		if (op_code != WSC_Start) {
4976d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
4986d49e1aeSJan Lentfer 				   "in WAIT_START state", op_code);
4996d49e1aeSJan Lentfer 			ret->ignore = TRUE;
5006d49e1aeSJan Lentfer 			return NULL;
5016d49e1aeSJan Lentfer 		}
5026d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
5036d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
5046d49e1aeSJan Lentfer 		/* Start message has empty payload, skip processing */
5056d49e1aeSJan Lentfer 		goto send_msg;
5066d49e1aeSJan Lentfer 	} else if (op_code == WSC_Start) {
5076d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
5086d49e1aeSJan Lentfer 			   op_code);
5096d49e1aeSJan Lentfer 		ret->ignore = TRUE;
5106d49e1aeSJan Lentfer 		return NULL;
5116d49e1aeSJan Lentfer 	}
5126d49e1aeSJan Lentfer 
5136d49e1aeSJan Lentfer 	if (data->in_buf &&
5146d49e1aeSJan Lentfer 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
5156d49e1aeSJan Lentfer 		ret->ignore = TRUE;
5166d49e1aeSJan Lentfer 		return NULL;
5176d49e1aeSJan Lentfer 	}
5186d49e1aeSJan Lentfer 
5196d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_MF) {
5206d49e1aeSJan Lentfer 		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
5216d49e1aeSJan Lentfer 						message_length, pos,
5226d49e1aeSJan Lentfer 						end - pos);
5236d49e1aeSJan Lentfer 	}
5246d49e1aeSJan Lentfer 
5256d49e1aeSJan Lentfer 	if (data->in_buf == NULL) {
5266d49e1aeSJan Lentfer 		/* Wrap unfragmented messages as wpabuf without extra copy */
5276d49e1aeSJan Lentfer 		wpabuf_set(&tmpbuf, pos, end - pos);
5286d49e1aeSJan Lentfer 		data->in_buf = &tmpbuf;
5296d49e1aeSJan Lentfer 	}
5306d49e1aeSJan Lentfer 
5316d49e1aeSJan Lentfer 	res = wps_process_msg(data->wps, op_code, data->in_buf);
5326d49e1aeSJan Lentfer 	switch (res) {
5336d49e1aeSJan Lentfer 	case WPS_DONE:
5346d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
5356d49e1aeSJan Lentfer 			   "successfully - wait for EAP failure");
5366d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
5376d49e1aeSJan Lentfer 		break;
5386d49e1aeSJan Lentfer 	case WPS_CONTINUE:
5396d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
5406d49e1aeSJan Lentfer 		break;
5416d49e1aeSJan Lentfer 	case WPS_FAILURE:
5426d49e1aeSJan Lentfer 	case WPS_PENDING:
5436d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
5446d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
5456d49e1aeSJan Lentfer 		break;
5466d49e1aeSJan Lentfer 	}
5476d49e1aeSJan Lentfer 
5486d49e1aeSJan Lentfer 	if (data->in_buf != &tmpbuf)
5496d49e1aeSJan Lentfer 		wpabuf_free(data->in_buf);
5506d49e1aeSJan Lentfer 	data->in_buf = NULL;
5516d49e1aeSJan Lentfer 
5526d49e1aeSJan Lentfer send_msg:
5536d49e1aeSJan Lentfer 	if (data->out_buf == NULL) {
5546d49e1aeSJan Lentfer 		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
5556d49e1aeSJan Lentfer 		if (data->out_buf == NULL) {
5566d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
5576d49e1aeSJan Lentfer 				   "message from WPS");
5586d49e1aeSJan Lentfer 			return NULL;
5596d49e1aeSJan Lentfer 		}
5606d49e1aeSJan Lentfer 		data->out_used = 0;
5616d49e1aeSJan Lentfer 	}
5626d49e1aeSJan Lentfer 
5636d49e1aeSJan Lentfer 	eap_wsc_state(data, MESG);
564*3ff40c12SJohn Marino 	r = eap_wsc_build_msg(data, ret, id);
565*3ff40c12SJohn Marino 	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
566*3ff40c12SJohn Marino 		/* Use reduced client timeout for WPS to avoid long wait */
567*3ff40c12SJohn Marino 		if (sm->ClientTimeout > 2)
568*3ff40c12SJohn Marino 			sm->ClientTimeout = 2;
569*3ff40c12SJohn Marino 	}
570*3ff40c12SJohn Marino 	return r;
5716d49e1aeSJan Lentfer }
5726d49e1aeSJan Lentfer 
5736d49e1aeSJan Lentfer 
5746d49e1aeSJan Lentfer int eap_peer_wsc_register(void)
5756d49e1aeSJan Lentfer {
5766d49e1aeSJan Lentfer 	struct eap_method *eap;
5776d49e1aeSJan Lentfer 	int ret;
5786d49e1aeSJan Lentfer 
5796d49e1aeSJan Lentfer 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
5806d49e1aeSJan Lentfer 				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
5816d49e1aeSJan Lentfer 				    "WSC");
5826d49e1aeSJan Lentfer 	if (eap == NULL)
5836d49e1aeSJan Lentfer 		return -1;
5846d49e1aeSJan Lentfer 
5856d49e1aeSJan Lentfer 	eap->init = eap_wsc_init;
5866d49e1aeSJan Lentfer 	eap->deinit = eap_wsc_deinit;
5876d49e1aeSJan Lentfer 	eap->process = eap_wsc_process;
5886d49e1aeSJan Lentfer 
5896d49e1aeSJan Lentfer 	ret = eap_peer_method_register(eap);
5906d49e1aeSJan Lentfer 	if (ret)
5916d49e1aeSJan Lentfer 		eap_peer_method_free(eap);
5926d49e1aeSJan Lentfer 	return ret;
5936d49e1aeSJan Lentfer }
594