xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_peer/eap_wsc.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * EAP-WSC peer for Wi-Fi Protected Setup
33ff40c12SJohn Marino  * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn 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 {
20*a1157835SDaniel Fojt 	enum { WAIT_START, MESG, WAIT_FRAG_ACK, 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 
eap_wsc_state_txt(int state)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 WAIT_FRAG_ACK:
406d49e1aeSJan Lentfer 		return "WAIT_FRAG_ACK";
416d49e1aeSJan Lentfer 	case FAIL:
426d49e1aeSJan Lentfer 		return "FAIL";
436d49e1aeSJan Lentfer 	default:
446d49e1aeSJan Lentfer 		return "?";
456d49e1aeSJan Lentfer 	}
466d49e1aeSJan Lentfer }
476d49e1aeSJan Lentfer 
486d49e1aeSJan Lentfer 
eap_wsc_state(struct eap_wsc_data * data,int state)496d49e1aeSJan Lentfer static void eap_wsc_state(struct eap_wsc_data *data, int state)
506d49e1aeSJan Lentfer {
516d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
526d49e1aeSJan Lentfer 		   eap_wsc_state_txt(data->state),
536d49e1aeSJan Lentfer 		   eap_wsc_state_txt(state));
546d49e1aeSJan Lentfer 	data->state = state;
556d49e1aeSJan Lentfer }
566d49e1aeSJan Lentfer 
576d49e1aeSJan Lentfer 
eap_wsc_new_ap_settings(struct wps_credential * cred,const char * params)583ff40c12SJohn Marino static int eap_wsc_new_ap_settings(struct wps_credential *cred,
593ff40c12SJohn Marino 				   const char *params)
603ff40c12SJohn Marino {
613ff40c12SJohn Marino 	const char *pos, *end;
623ff40c12SJohn Marino 	size_t len;
633ff40c12SJohn Marino 
643ff40c12SJohn Marino 	os_memset(cred, 0, sizeof(*cred));
653ff40c12SJohn Marino 
663ff40c12SJohn Marino 	pos = os_strstr(params, "new_ssid=");
673ff40c12SJohn Marino 	if (pos == NULL)
683ff40c12SJohn Marino 		return 0;
693ff40c12SJohn Marino 	pos += 9;
703ff40c12SJohn Marino 	end = os_strchr(pos, ' ');
713ff40c12SJohn Marino 	if (end == NULL)
723ff40c12SJohn Marino 		len = os_strlen(pos);
733ff40c12SJohn Marino 	else
743ff40c12SJohn Marino 		len = end - pos;
753ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
763ff40c12SJohn Marino 	    hexstr2bin(pos, cred->ssid, len / 2)) {
773ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
783ff40c12SJohn Marino 		return -1;
793ff40c12SJohn Marino 	}
803ff40c12SJohn Marino 	cred->ssid_len = len / 2;
813ff40c12SJohn Marino 
823ff40c12SJohn Marino 	pos = os_strstr(params, "new_auth=");
833ff40c12SJohn Marino 	if (pos == NULL) {
843ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
853ff40c12SJohn Marino 		return -1;
863ff40c12SJohn Marino 	}
873ff40c12SJohn Marino 	if (os_strncmp(pos + 9, "OPEN", 4) == 0)
883ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_OPEN;
893ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
903ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_WPAPSK;
913ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
923ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_WPA2PSK;
933ff40c12SJohn Marino 	else {
943ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
953ff40c12SJohn Marino 		return -1;
963ff40c12SJohn Marino 	}
973ff40c12SJohn Marino 
983ff40c12SJohn Marino 	pos = os_strstr(params, "new_encr=");
993ff40c12SJohn Marino 	if (pos == NULL) {
1003ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
1013ff40c12SJohn Marino 		return -1;
1023ff40c12SJohn Marino 	}
1033ff40c12SJohn Marino 	if (os_strncmp(pos + 9, "NONE", 4) == 0)
1043ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_NONE;
105*a1157835SDaniel Fojt #ifdef CONFIG_TESTING_OPTIONS
1063ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "WEP", 3) == 0)
1073ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_WEP;
108*a1157835SDaniel Fojt #endif /* CONFIG_TESTING_OPTIONS */
1093ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
1103ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_TKIP;
1113ff40c12SJohn Marino 	else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
1123ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_AES;
1133ff40c12SJohn Marino 	else {
1143ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
1153ff40c12SJohn Marino 		return -1;
1163ff40c12SJohn Marino 	}
1173ff40c12SJohn Marino 
1183ff40c12SJohn Marino 	pos = os_strstr(params, "new_key=");
1193ff40c12SJohn Marino 	if (pos == NULL)
1203ff40c12SJohn Marino 		return 0;
1213ff40c12SJohn Marino 	pos += 8;
1223ff40c12SJohn Marino 	end = os_strchr(pos, ' ');
1233ff40c12SJohn Marino 	if (end == NULL)
1243ff40c12SJohn Marino 		len = os_strlen(pos);
1253ff40c12SJohn Marino 	else
1263ff40c12SJohn Marino 		len = end - pos;
1273ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred->key) ||
1283ff40c12SJohn Marino 	    hexstr2bin(pos, cred->key, len / 2)) {
1293ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
1303ff40c12SJohn Marino 		return -1;
1313ff40c12SJohn Marino 	}
1323ff40c12SJohn Marino 	cred->key_len = len / 2;
1333ff40c12SJohn Marino 
1343ff40c12SJohn Marino 	return 1;
1353ff40c12SJohn Marino }
1363ff40c12SJohn Marino 
1373ff40c12SJohn Marino 
eap_wsc_init(struct eap_sm * sm)1386d49e1aeSJan Lentfer static void * eap_wsc_init(struct eap_sm *sm)
1396d49e1aeSJan Lentfer {
1406d49e1aeSJan Lentfer 	struct eap_wsc_data *data;
1416d49e1aeSJan Lentfer 	const u8 *identity;
1426d49e1aeSJan Lentfer 	size_t identity_len;
1436d49e1aeSJan Lentfer 	int registrar;
1446d49e1aeSJan Lentfer 	struct wps_config cfg;
1453ff40c12SJohn Marino 	const char *pos, *end;
1466d49e1aeSJan Lentfer 	const char *phase1;
1476d49e1aeSJan Lentfer 	struct wps_context *wps;
1483ff40c12SJohn Marino 	struct wps_credential new_ap_settings;
1493ff40c12SJohn Marino 	int res;
1503ff40c12SJohn Marino 	int nfc = 0;
1513ff40c12SJohn Marino 	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
1526d49e1aeSJan Lentfer 
1536d49e1aeSJan Lentfer 	wps = sm->wps;
1546d49e1aeSJan Lentfer 	if (wps == NULL) {
1556d49e1aeSJan Lentfer 		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
1566d49e1aeSJan Lentfer 		return NULL;
1576d49e1aeSJan Lentfer 	}
1586d49e1aeSJan Lentfer 
1596d49e1aeSJan Lentfer 	identity = eap_get_config_identity(sm, &identity_len);
1606d49e1aeSJan Lentfer 
1616d49e1aeSJan Lentfer 	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
1626d49e1aeSJan Lentfer 	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
1636d49e1aeSJan Lentfer 		registrar = 1; /* Supplicant is Registrar */
1646d49e1aeSJan Lentfer 	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
1656d49e1aeSJan Lentfer 	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
1666d49e1aeSJan Lentfer 		registrar = 0; /* Supplicant is Enrollee */
1676d49e1aeSJan Lentfer 	else {
1686d49e1aeSJan Lentfer 		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
1696d49e1aeSJan Lentfer 				  identity, identity_len);
1706d49e1aeSJan Lentfer 		return NULL;
1716d49e1aeSJan Lentfer 	}
1726d49e1aeSJan Lentfer 
1736d49e1aeSJan Lentfer 	data = os_zalloc(sizeof(*data));
1746d49e1aeSJan Lentfer 	if (data == NULL)
1756d49e1aeSJan Lentfer 		return NULL;
1766d49e1aeSJan Lentfer 	data->state = registrar ? MESG : WAIT_START;
1776d49e1aeSJan Lentfer 	data->registrar = registrar;
1786d49e1aeSJan Lentfer 	data->wps_ctx = wps;
1796d49e1aeSJan Lentfer 
1806d49e1aeSJan Lentfer 	os_memset(&cfg, 0, sizeof(cfg));
1816d49e1aeSJan Lentfer 	cfg.wps = wps;
1826d49e1aeSJan Lentfer 	cfg.registrar = registrar;
1836d49e1aeSJan Lentfer 
1846d49e1aeSJan Lentfer 	phase1 = eap_get_config_phase1(sm);
1856d49e1aeSJan Lentfer 	if (phase1 == NULL) {
1866d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
1876d49e1aeSJan Lentfer 			   "set");
1886d49e1aeSJan Lentfer 		os_free(data);
1896d49e1aeSJan Lentfer 		return NULL;
1906d49e1aeSJan Lentfer 	}
1916d49e1aeSJan Lentfer 
1926d49e1aeSJan Lentfer 	pos = os_strstr(phase1, "pin=");
1936d49e1aeSJan Lentfer 	if (pos) {
1946d49e1aeSJan Lentfer 		pos += 4;
1956d49e1aeSJan Lentfer 		cfg.pin = (const u8 *) pos;
1966d49e1aeSJan Lentfer 		while (*pos != '\0' && *pos != ' ')
1976d49e1aeSJan Lentfer 			pos++;
1986d49e1aeSJan Lentfer 		cfg.pin_len = pos - (const char *) cfg.pin;
1993ff40c12SJohn Marino 		if (cfg.pin_len == 6 &&
2003ff40c12SJohn Marino 		    os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
2013ff40c12SJohn Marino 			cfg.pin = NULL;
2023ff40c12SJohn Marino 			cfg.pin_len = 0;
2033ff40c12SJohn Marino 			nfc = 1;
2043ff40c12SJohn Marino 		}
2056d49e1aeSJan Lentfer 	} else {
2066d49e1aeSJan Lentfer 		pos = os_strstr(phase1, "pbc=1");
2076d49e1aeSJan Lentfer 		if (pos)
2086d49e1aeSJan Lentfer 			cfg.pbc = 1;
2096d49e1aeSJan Lentfer 	}
2106d49e1aeSJan Lentfer 
2113ff40c12SJohn Marino 	pos = os_strstr(phase1, "dev_pw_id=");
2123ff40c12SJohn Marino 	if (pos) {
2133ff40c12SJohn Marino 		u16 id = atoi(pos + 10);
2143ff40c12SJohn Marino 		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
2153ff40c12SJohn Marino 			nfc = 1;
2163ff40c12SJohn Marino 		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
2173ff40c12SJohn Marino 			cfg.dev_pw_id = id;
2183ff40c12SJohn Marino 	}
2193ff40c12SJohn Marino 
2203ff40c12SJohn Marino 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
2216d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
2226d49e1aeSJan Lentfer 			   "configuration data");
2236d49e1aeSJan Lentfer 		os_free(data);
2246d49e1aeSJan Lentfer 		return NULL;
2256d49e1aeSJan Lentfer 	}
2266d49e1aeSJan Lentfer 
2273ff40c12SJohn Marino 	pos = os_strstr(phase1, " pkhash=");
2283ff40c12SJohn Marino 	if (pos) {
2293ff40c12SJohn Marino 		size_t len;
2303ff40c12SJohn Marino 		pos += 8;
2313ff40c12SJohn Marino 		end = os_strchr(pos, ' ');
2323ff40c12SJohn Marino 		if (end)
2333ff40c12SJohn Marino 			len = end - pos;
2343ff40c12SJohn Marino 		else
2353ff40c12SJohn Marino 			len = os_strlen(pos);
2363ff40c12SJohn Marino 		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
2373ff40c12SJohn Marino 		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
2383ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
2396d49e1aeSJan Lentfer 			os_free(data);
2406d49e1aeSJan Lentfer 			return NULL;
2416d49e1aeSJan Lentfer 		}
2423ff40c12SJohn Marino 		cfg.peer_pubkey_hash = pkhash;
2433ff40c12SJohn Marino 	}
2443ff40c12SJohn Marino 
2453ff40c12SJohn Marino 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
2463ff40c12SJohn Marino 	if (res < 0) {
2473ff40c12SJohn Marino 		os_free(data);
2483ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
2493ff40c12SJohn Marino 			   "settings");
2503ff40c12SJohn Marino 		return NULL;
2513ff40c12SJohn Marino 	}
2523ff40c12SJohn Marino 	if (res == 1) {
2533ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
2543ff40c12SJohn Marino 			   "WPS");
2553ff40c12SJohn Marino 		cfg.new_ap_settings = &new_ap_settings;
2563ff40c12SJohn Marino 	}
2573ff40c12SJohn Marino 
258*a1157835SDaniel Fojt 	if (os_strstr(phase1, "multi_ap=1"))
259*a1157835SDaniel Fojt 		cfg.multi_ap_backhaul_sta = 1;
260*a1157835SDaniel Fojt 
2613ff40c12SJohn Marino 	data->wps = wps_init(&cfg);
2623ff40c12SJohn Marino 	if (data->wps == NULL) {
2633ff40c12SJohn Marino 		os_free(data);
2643ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
2653ff40c12SJohn Marino 		return NULL;
2663ff40c12SJohn Marino 	}
2673ff40c12SJohn Marino 	res = eap_get_config_fragment_size(sm);
2683ff40c12SJohn Marino 	if (res > 0)
2693ff40c12SJohn Marino 		data->fragment_size = res;
2703ff40c12SJohn Marino 	else
2716d49e1aeSJan Lentfer 		data->fragment_size = WSC_FRAGMENT_SIZE;
2723ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
2733ff40c12SJohn Marino 		   (unsigned int) data->fragment_size);
2746d49e1aeSJan Lentfer 
2756d49e1aeSJan Lentfer 	if (registrar && cfg.pin) {
2763ff40c12SJohn Marino 		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
2776d49e1aeSJan Lentfer 				      cfg.pin, cfg.pin_len, 0);
2786d49e1aeSJan Lentfer 	}
2796d49e1aeSJan Lentfer 
2803ff40c12SJohn Marino 	/* Use reduced client timeout for WPS to avoid long wait */
2813ff40c12SJohn Marino 	if (sm->ClientTimeout > 30)
2823ff40c12SJohn Marino 		sm->ClientTimeout = 30;
2833ff40c12SJohn Marino 
2846d49e1aeSJan Lentfer 	return data;
2856d49e1aeSJan Lentfer }
2866d49e1aeSJan Lentfer 
2876d49e1aeSJan Lentfer 
eap_wsc_deinit(struct eap_sm * sm,void * priv)2886d49e1aeSJan Lentfer static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
2896d49e1aeSJan Lentfer {
2906d49e1aeSJan Lentfer 	struct eap_wsc_data *data = priv;
2916d49e1aeSJan Lentfer 	wpabuf_free(data->in_buf);
2926d49e1aeSJan Lentfer 	wpabuf_free(data->out_buf);
2936d49e1aeSJan Lentfer 	wps_deinit(data->wps);
2946d49e1aeSJan Lentfer 	os_free(data->wps_ctx->network_key);
2956d49e1aeSJan Lentfer 	data->wps_ctx->network_key = NULL;
2966d49e1aeSJan Lentfer 	os_free(data);
2976d49e1aeSJan Lentfer }
2986d49e1aeSJan Lentfer 
2996d49e1aeSJan Lentfer 
eap_wsc_build_msg(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id)3006d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
3016d49e1aeSJan Lentfer 					 struct eap_method_ret *ret, u8 id)
3026d49e1aeSJan Lentfer {
3036d49e1aeSJan Lentfer 	struct wpabuf *resp;
3046d49e1aeSJan Lentfer 	u8 flags;
3056d49e1aeSJan Lentfer 	size_t send_len, plen;
3066d49e1aeSJan Lentfer 
3076d49e1aeSJan Lentfer 	ret->ignore = FALSE;
3086d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
3096d49e1aeSJan Lentfer 	ret->allowNotifications = TRUE;
3106d49e1aeSJan Lentfer 
3116d49e1aeSJan Lentfer 	flags = 0;
3126d49e1aeSJan Lentfer 	send_len = wpabuf_len(data->out_buf) - data->out_used;
3136d49e1aeSJan Lentfer 	if (2 + send_len > data->fragment_size) {
3146d49e1aeSJan Lentfer 		send_len = data->fragment_size - 2;
3156d49e1aeSJan Lentfer 		flags |= WSC_FLAGS_MF;
3166d49e1aeSJan Lentfer 		if (data->out_used == 0) {
3176d49e1aeSJan Lentfer 			flags |= WSC_FLAGS_LF;
3186d49e1aeSJan Lentfer 			send_len -= 2;
3196d49e1aeSJan Lentfer 		}
3206d49e1aeSJan Lentfer 	}
3216d49e1aeSJan Lentfer 	plen = 2 + send_len;
3226d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF)
3236d49e1aeSJan Lentfer 		plen += 2;
3246d49e1aeSJan Lentfer 	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
3256d49e1aeSJan Lentfer 			     EAP_CODE_RESPONSE, id);
3266d49e1aeSJan Lentfer 	if (resp == NULL)
3276d49e1aeSJan Lentfer 		return NULL;
3286d49e1aeSJan Lentfer 
3296d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
3306d49e1aeSJan Lentfer 	wpabuf_put_u8(resp, flags); /* Flags */
3316d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF)
3326d49e1aeSJan Lentfer 		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
3336d49e1aeSJan Lentfer 
3346d49e1aeSJan Lentfer 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
3356d49e1aeSJan Lentfer 			send_len);
3366d49e1aeSJan Lentfer 	data->out_used += send_len;
3376d49e1aeSJan Lentfer 
3386d49e1aeSJan Lentfer 	ret->methodState = METHOD_MAY_CONT;
3396d49e1aeSJan Lentfer 	ret->decision = DECISION_FAIL;
3406d49e1aeSJan Lentfer 
3416d49e1aeSJan Lentfer 	if (data->out_used == wpabuf_len(data->out_buf)) {
3426d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
3436d49e1aeSJan Lentfer 			   "(message sent completely)",
3446d49e1aeSJan Lentfer 			   (unsigned long) send_len);
3456d49e1aeSJan Lentfer 		wpabuf_free(data->out_buf);
3466d49e1aeSJan Lentfer 		data->out_buf = NULL;
3476d49e1aeSJan Lentfer 		data->out_used = 0;
3486d49e1aeSJan Lentfer 		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
3496d49e1aeSJan Lentfer 		    data->out_op_code == WSC_NACK ||
3506d49e1aeSJan Lentfer 		    data->out_op_code == WSC_Done) {
3516d49e1aeSJan Lentfer 			eap_wsc_state(data, FAIL);
3526d49e1aeSJan Lentfer 			ret->methodState = METHOD_DONE;
3536d49e1aeSJan Lentfer 		} else
3546d49e1aeSJan Lentfer 			eap_wsc_state(data, MESG);
3556d49e1aeSJan Lentfer 	} else {
3566d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
3576d49e1aeSJan Lentfer 			   "(%lu more to send)", (unsigned long) send_len,
3586d49e1aeSJan Lentfer 			   (unsigned long) wpabuf_len(data->out_buf) -
3596d49e1aeSJan Lentfer 			   data->out_used);
3606d49e1aeSJan Lentfer 		eap_wsc_state(data, WAIT_FRAG_ACK);
3616d49e1aeSJan Lentfer 	}
3626d49e1aeSJan Lentfer 
3636d49e1aeSJan Lentfer 	return resp;
3646d49e1aeSJan Lentfer }
3656d49e1aeSJan Lentfer 
3666d49e1aeSJan Lentfer 
eap_wsc_process_cont(struct eap_wsc_data * data,const u8 * buf,size_t len,u8 op_code)3676d49e1aeSJan Lentfer static int eap_wsc_process_cont(struct eap_wsc_data *data,
3686d49e1aeSJan Lentfer 				const u8 *buf, size_t len, u8 op_code)
3696d49e1aeSJan Lentfer {
3706d49e1aeSJan Lentfer 	/* Process continuation of a pending message */
3716d49e1aeSJan Lentfer 	if (op_code != data->in_op_code) {
3726d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
3736d49e1aeSJan Lentfer 			   "fragment (expected %d)",
3746d49e1aeSJan Lentfer 			   op_code, data->in_op_code);
3756d49e1aeSJan Lentfer 		return -1;
3766d49e1aeSJan Lentfer 	}
3776d49e1aeSJan Lentfer 
3786d49e1aeSJan Lentfer 	if (len > wpabuf_tailroom(data->in_buf)) {
3796d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
3806d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
3816d49e1aeSJan Lentfer 		return -1;
3826d49e1aeSJan Lentfer 	}
3836d49e1aeSJan Lentfer 
3846d49e1aeSJan Lentfer 	wpabuf_put_data(data->in_buf, buf, len);
3856d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
3866d49e1aeSJan Lentfer 		   "for %lu bytes more", (unsigned long) len,
3876d49e1aeSJan Lentfer 		   (unsigned long) wpabuf_tailroom(data->in_buf));
3886d49e1aeSJan Lentfer 
3896d49e1aeSJan Lentfer 	return 0;
3906d49e1aeSJan Lentfer }
3916d49e1aeSJan Lentfer 
3926d49e1aeSJan Lentfer 
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)3936d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
3946d49e1aeSJan Lentfer 						struct eap_method_ret *ret,
3956d49e1aeSJan Lentfer 						u8 id, u8 flags, u8 op_code,
3966d49e1aeSJan Lentfer 						u16 message_length,
3976d49e1aeSJan Lentfer 						const u8 *buf, size_t len)
3986d49e1aeSJan Lentfer {
3996d49e1aeSJan Lentfer 	/* Process a fragment that is not the last one of the message */
4006d49e1aeSJan Lentfer 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
4016d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
4026d49e1aeSJan Lentfer 			   "fragmented packet");
4036d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4046d49e1aeSJan Lentfer 		return NULL;
4056d49e1aeSJan Lentfer 	}
4066d49e1aeSJan Lentfer 
4076d49e1aeSJan Lentfer 	if (data->in_buf == NULL) {
4086d49e1aeSJan Lentfer 		/* First fragment of the message */
4096d49e1aeSJan Lentfer 		data->in_buf = wpabuf_alloc(message_length);
4106d49e1aeSJan Lentfer 		if (data->in_buf == NULL) {
4116d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
4126d49e1aeSJan Lentfer 				   "message");
4136d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4146d49e1aeSJan Lentfer 			return NULL;
4156d49e1aeSJan Lentfer 		}
4166d49e1aeSJan Lentfer 		data->in_op_code = op_code;
4176d49e1aeSJan Lentfer 		wpabuf_put_data(data->in_buf, buf, len);
4186d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
4196d49e1aeSJan Lentfer 			   "fragment, waiting for %lu bytes more",
4206d49e1aeSJan Lentfer 			   (unsigned long) len,
4216d49e1aeSJan Lentfer 			   (unsigned long) wpabuf_tailroom(data->in_buf));
4226d49e1aeSJan Lentfer 	}
4236d49e1aeSJan Lentfer 
4246d49e1aeSJan Lentfer 	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
4256d49e1aeSJan Lentfer }
4266d49e1aeSJan Lentfer 
4276d49e1aeSJan Lentfer 
eap_wsc_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)4286d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
4296d49e1aeSJan Lentfer 				       struct eap_method_ret *ret,
4306d49e1aeSJan Lentfer 				       const struct wpabuf *reqData)
4316d49e1aeSJan Lentfer {
4326d49e1aeSJan Lentfer 	struct eap_wsc_data *data = priv;
4336d49e1aeSJan Lentfer 	const u8 *start, *pos, *end;
4346d49e1aeSJan Lentfer 	size_t len;
4356d49e1aeSJan Lentfer 	u8 op_code, flags, id;
4366d49e1aeSJan Lentfer 	u16 message_length = 0;
4376d49e1aeSJan Lentfer 	enum wps_process_res res;
4386d49e1aeSJan Lentfer 	struct wpabuf tmpbuf;
4393ff40c12SJohn Marino 	struct wpabuf *r;
4406d49e1aeSJan Lentfer 
4416d49e1aeSJan Lentfer 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
4426d49e1aeSJan Lentfer 			       &len);
4436d49e1aeSJan Lentfer 	if (pos == NULL || len < 2) {
4446d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4456d49e1aeSJan Lentfer 		return NULL;
4466d49e1aeSJan Lentfer 	}
4476d49e1aeSJan Lentfer 
4486d49e1aeSJan Lentfer 	id = eap_get_id(reqData);
4496d49e1aeSJan Lentfer 
4506d49e1aeSJan Lentfer 	start = pos;
4516d49e1aeSJan Lentfer 	end = start + len;
4526d49e1aeSJan Lentfer 
4536d49e1aeSJan Lentfer 	op_code = *pos++;
4546d49e1aeSJan Lentfer 	flags = *pos++;
4556d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_LF) {
4566d49e1aeSJan Lentfer 		if (end - pos < 2) {
4576d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
4586d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4596d49e1aeSJan Lentfer 			return NULL;
4606d49e1aeSJan Lentfer 		}
4616d49e1aeSJan Lentfer 		message_length = WPA_GET_BE16(pos);
4626d49e1aeSJan Lentfer 		pos += 2;
4636d49e1aeSJan Lentfer 
464*a1157835SDaniel Fojt 		if (message_length < end - pos || message_length > 50000) {
4656d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
4666d49e1aeSJan Lentfer 				   "Length");
4676d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4686d49e1aeSJan Lentfer 			return NULL;
4696d49e1aeSJan Lentfer 		}
4706d49e1aeSJan Lentfer 	}
4716d49e1aeSJan Lentfer 
4726d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
4736d49e1aeSJan Lentfer 		   "Flags 0x%x Message Length %d",
4746d49e1aeSJan Lentfer 		   op_code, flags, message_length);
4756d49e1aeSJan Lentfer 
4766d49e1aeSJan Lentfer 	if (data->state == WAIT_FRAG_ACK) {
4776d49e1aeSJan Lentfer 		if (op_code != WSC_FRAG_ACK) {
4786d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
4796d49e1aeSJan Lentfer 				   "in WAIT_FRAG_ACK state", op_code);
4806d49e1aeSJan Lentfer 			ret->ignore = TRUE;
4816d49e1aeSJan Lentfer 			return NULL;
4826d49e1aeSJan Lentfer 		}
4836d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
4846d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
4856d49e1aeSJan Lentfer 		return eap_wsc_build_msg(data, ret, id);
4866d49e1aeSJan Lentfer 	}
4876d49e1aeSJan Lentfer 
4886d49e1aeSJan Lentfer 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
4896d49e1aeSJan Lentfer 	    op_code != WSC_Done && op_code != WSC_Start) {
4906d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
4916d49e1aeSJan Lentfer 			   op_code);
4926d49e1aeSJan Lentfer 		ret->ignore = TRUE;
4936d49e1aeSJan Lentfer 		return NULL;
4946d49e1aeSJan Lentfer 	}
4956d49e1aeSJan Lentfer 
4966d49e1aeSJan Lentfer 	if (data->state == WAIT_START) {
4976d49e1aeSJan Lentfer 		if (op_code != WSC_Start) {
4986d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
4996d49e1aeSJan Lentfer 				   "in WAIT_START state", op_code);
5006d49e1aeSJan Lentfer 			ret->ignore = TRUE;
5016d49e1aeSJan Lentfer 			return NULL;
5026d49e1aeSJan Lentfer 		}
5036d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
5046d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
5056d49e1aeSJan Lentfer 		/* Start message has empty payload, skip processing */
5066d49e1aeSJan Lentfer 		goto send_msg;
5076d49e1aeSJan Lentfer 	} else if (op_code == WSC_Start) {
5086d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
5096d49e1aeSJan Lentfer 			   op_code);
5106d49e1aeSJan Lentfer 		ret->ignore = TRUE;
5116d49e1aeSJan Lentfer 		return NULL;
5126d49e1aeSJan Lentfer 	}
5136d49e1aeSJan Lentfer 
5146d49e1aeSJan Lentfer 	if (data->in_buf &&
5156d49e1aeSJan Lentfer 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
5166d49e1aeSJan Lentfer 		ret->ignore = TRUE;
5176d49e1aeSJan Lentfer 		return NULL;
5186d49e1aeSJan Lentfer 	}
5196d49e1aeSJan Lentfer 
5206d49e1aeSJan Lentfer 	if (flags & WSC_FLAGS_MF) {
5216d49e1aeSJan Lentfer 		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
5226d49e1aeSJan Lentfer 						message_length, pos,
5236d49e1aeSJan Lentfer 						end - pos);
5246d49e1aeSJan Lentfer 	}
5256d49e1aeSJan Lentfer 
5266d49e1aeSJan Lentfer 	if (data->in_buf == NULL) {
5276d49e1aeSJan Lentfer 		/* Wrap unfragmented messages as wpabuf without extra copy */
5286d49e1aeSJan Lentfer 		wpabuf_set(&tmpbuf, pos, end - pos);
5296d49e1aeSJan Lentfer 		data->in_buf = &tmpbuf;
5306d49e1aeSJan Lentfer 	}
5316d49e1aeSJan Lentfer 
5326d49e1aeSJan Lentfer 	res = wps_process_msg(data->wps, op_code, data->in_buf);
5336d49e1aeSJan Lentfer 	switch (res) {
5346d49e1aeSJan Lentfer 	case WPS_DONE:
5356d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
5366d49e1aeSJan Lentfer 			   "successfully - wait for EAP failure");
5376d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
5386d49e1aeSJan Lentfer 		break;
5396d49e1aeSJan Lentfer 	case WPS_CONTINUE:
5406d49e1aeSJan Lentfer 		eap_wsc_state(data, MESG);
5416d49e1aeSJan Lentfer 		break;
5426d49e1aeSJan Lentfer 	case WPS_FAILURE:
5436d49e1aeSJan Lentfer 	case WPS_PENDING:
5446d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
5456d49e1aeSJan Lentfer 		eap_wsc_state(data, FAIL);
5466d49e1aeSJan Lentfer 		break;
5476d49e1aeSJan Lentfer 	}
5486d49e1aeSJan Lentfer 
5496d49e1aeSJan Lentfer 	if (data->in_buf != &tmpbuf)
5506d49e1aeSJan Lentfer 		wpabuf_free(data->in_buf);
5516d49e1aeSJan Lentfer 	data->in_buf = NULL;
5526d49e1aeSJan Lentfer 
5536d49e1aeSJan Lentfer send_msg:
5546d49e1aeSJan Lentfer 	if (data->out_buf == NULL) {
5556d49e1aeSJan Lentfer 		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
5566d49e1aeSJan Lentfer 		if (data->out_buf == NULL) {
5576d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
5586d49e1aeSJan Lentfer 				   "message from WPS");
559*a1157835SDaniel Fojt 			eap_wsc_state(data, FAIL);
560*a1157835SDaniel Fojt 			ret->methodState = METHOD_DONE;
561*a1157835SDaniel Fojt 			ret->decision = DECISION_FAIL;
5626d49e1aeSJan Lentfer 			return NULL;
5636d49e1aeSJan Lentfer 		}
5646d49e1aeSJan Lentfer 		data->out_used = 0;
5656d49e1aeSJan Lentfer 	}
5666d49e1aeSJan Lentfer 
5676d49e1aeSJan Lentfer 	eap_wsc_state(data, MESG);
5683ff40c12SJohn Marino 	r = eap_wsc_build_msg(data, ret, id);
5693ff40c12SJohn Marino 	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
5703ff40c12SJohn Marino 		/* Use reduced client timeout for WPS to avoid long wait */
5713ff40c12SJohn Marino 		if (sm->ClientTimeout > 2)
5723ff40c12SJohn Marino 			sm->ClientTimeout = 2;
5733ff40c12SJohn Marino 	}
5743ff40c12SJohn Marino 	return r;
5756d49e1aeSJan Lentfer }
5766d49e1aeSJan Lentfer 
5776d49e1aeSJan Lentfer 
eap_peer_wsc_register(void)5786d49e1aeSJan Lentfer int eap_peer_wsc_register(void)
5796d49e1aeSJan Lentfer {
5806d49e1aeSJan Lentfer 	struct eap_method *eap;
5816d49e1aeSJan Lentfer 
5826d49e1aeSJan Lentfer 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
5836d49e1aeSJan Lentfer 				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
5846d49e1aeSJan Lentfer 				    "WSC");
5856d49e1aeSJan Lentfer 	if (eap == NULL)
5866d49e1aeSJan Lentfer 		return -1;
5876d49e1aeSJan Lentfer 
5886d49e1aeSJan Lentfer 	eap->init = eap_wsc_init;
5896d49e1aeSJan Lentfer 	eap->deinit = eap_wsc_deinit;
5906d49e1aeSJan Lentfer 	eap->process = eap_wsc_process;
5916d49e1aeSJan Lentfer 
592*a1157835SDaniel Fojt 	return eap_peer_method_register(eap);
5936d49e1aeSJan Lentfer }
594