xref: /dflybsd-src/contrib/wpa_supplicant/src/wps/wps_attr_parse.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * Wi-Fi Protected Setup - attribute parsing
36d49e1aeSJan Lentfer  * Copyright (c) 2008, 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"
123ff40c12SJohn Marino #include "wps_defs.h"
133ff40c12SJohn Marino #include "wps_attr_parse.h"
143ff40c12SJohn Marino 
153ff40c12SJohn Marino #ifndef CONFIG_WPS_STRICT
163ff40c12SJohn Marino #define WPS_WORKAROUNDS
173ff40c12SJohn Marino #endif /* CONFIG_WPS_STRICT */
183ff40c12SJohn Marino 
193ff40c12SJohn Marino 
wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr * attr,u8 id,u8 len,const u8 * pos)203ff40c12SJohn Marino static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
213ff40c12SJohn Marino 					  u8 id, u8 len, const u8 *pos)
223ff40c12SJohn Marino {
233ff40c12SJohn Marino 	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
243ff40c12SJohn Marino 		   id, len);
253ff40c12SJohn Marino 	switch (id) {
263ff40c12SJohn Marino 	case WFA_ELEM_VERSION2:
273ff40c12SJohn Marino 		if (len != 1) {
283ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
293ff40c12SJohn Marino 				   "%u", len);
303ff40c12SJohn Marino 			return -1;
313ff40c12SJohn Marino 		}
323ff40c12SJohn Marino 		attr->version2 = pos;
333ff40c12SJohn Marino 		break;
343ff40c12SJohn Marino 	case WFA_ELEM_AUTHORIZEDMACS:
353ff40c12SJohn Marino 		attr->authorized_macs = pos;
363ff40c12SJohn Marino 		attr->authorized_macs_len = len;
373ff40c12SJohn Marino 		break;
383ff40c12SJohn Marino 	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
393ff40c12SJohn Marino 		if (len != 1) {
403ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
413ff40c12SJohn Marino 				   "Shareable length %u", len);
423ff40c12SJohn Marino 			return -1;
433ff40c12SJohn Marino 		}
443ff40c12SJohn Marino 		attr->network_key_shareable = pos;
453ff40c12SJohn Marino 		break;
463ff40c12SJohn Marino 	case WFA_ELEM_REQUEST_TO_ENROLL:
473ff40c12SJohn Marino 		if (len != 1) {
483ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
493ff40c12SJohn Marino 				   "length %u", len);
503ff40c12SJohn Marino 			return -1;
513ff40c12SJohn Marino 		}
523ff40c12SJohn Marino 		attr->request_to_enroll = pos;
533ff40c12SJohn Marino 		break;
543ff40c12SJohn Marino 	case WFA_ELEM_SETTINGS_DELAY_TIME:
553ff40c12SJohn Marino 		if (len != 1) {
563ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
573ff40c12SJohn Marino 				   "Time length %u", len);
583ff40c12SJohn Marino 			return -1;
593ff40c12SJohn Marino 		}
603ff40c12SJohn Marino 		attr->settings_delay_time = pos;
613ff40c12SJohn Marino 		break;
62*a1157835SDaniel Fojt 	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
63*a1157835SDaniel Fojt 		if (len != 2) {
64*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
65*a1157835SDaniel Fojt 				   len);
66*a1157835SDaniel Fojt 			return -1;
67*a1157835SDaniel Fojt 		}
68*a1157835SDaniel Fojt 		attr->registrar_configuration_methods = pos;
69*a1157835SDaniel Fojt 		break;
70*a1157835SDaniel Fojt 	case WFA_ELEM_MULTI_AP:
71*a1157835SDaniel Fojt 		if (len != 1) {
72*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
73*a1157835SDaniel Fojt 				   "WPS: Invalid Multi-AP Extension length %u",
74*a1157835SDaniel Fojt 				   len);
75*a1157835SDaniel Fojt 			return -1;
76*a1157835SDaniel Fojt 		}
77*a1157835SDaniel Fojt 		attr->multi_ap_ext = *pos;
78*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
79*a1157835SDaniel Fojt 			   attr->multi_ap_ext);
80*a1157835SDaniel Fojt 		break;
813ff40c12SJohn Marino 	default:
823ff40c12SJohn Marino 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
833ff40c12SJohn Marino 			   "Extension subelement %u", id);
843ff40c12SJohn Marino 		break;
853ff40c12SJohn Marino 	}
863ff40c12SJohn Marino 
873ff40c12SJohn Marino 	return 0;
883ff40c12SJohn Marino }
893ff40c12SJohn Marino 
903ff40c12SJohn Marino 
wps_parse_vendor_ext_wfa(struct wps_parse_attr * attr,const u8 * pos,u16 len)913ff40c12SJohn Marino static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
923ff40c12SJohn Marino 				    u16 len)
933ff40c12SJohn Marino {
943ff40c12SJohn Marino 	const u8 *end = pos + len;
953ff40c12SJohn Marino 	u8 id, elen;
963ff40c12SJohn Marino 
97*a1157835SDaniel Fojt 	while (end - pos >= 2) {
983ff40c12SJohn Marino 		id = *pos++;
993ff40c12SJohn Marino 		elen = *pos++;
100*a1157835SDaniel Fojt 		if (elen > end - pos)
1013ff40c12SJohn Marino 			break;
1023ff40c12SJohn Marino 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
1033ff40c12SJohn Marino 			return -1;
1043ff40c12SJohn Marino 		pos += elen;
1053ff40c12SJohn Marino 	}
1063ff40c12SJohn Marino 
1073ff40c12SJohn Marino 	return 0;
1083ff40c12SJohn Marino }
1093ff40c12SJohn Marino 
1103ff40c12SJohn Marino 
wps_parse_vendor_ext(struct wps_parse_attr * attr,const u8 * pos,u16 len)1113ff40c12SJohn Marino static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
1123ff40c12SJohn Marino 				u16 len)
1133ff40c12SJohn Marino {
1143ff40c12SJohn Marino 	u32 vendor_id;
1153ff40c12SJohn Marino 
1163ff40c12SJohn Marino 	if (len < 3) {
1173ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
1183ff40c12SJohn Marino 		return 0;
1193ff40c12SJohn Marino 	}
1203ff40c12SJohn Marino 
1213ff40c12SJohn Marino 	vendor_id = WPA_GET_BE24(pos);
1223ff40c12SJohn Marino 	switch (vendor_id) {
1233ff40c12SJohn Marino 	case WPS_VENDOR_ID_WFA:
1243ff40c12SJohn Marino 		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
1253ff40c12SJohn Marino 	}
1263ff40c12SJohn Marino 
1273ff40c12SJohn Marino 	/* Handle unknown vendor extensions */
1283ff40c12SJohn Marino 
1293ff40c12SJohn Marino 	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
1303ff40c12SJohn Marino 		   vendor_id);
1313ff40c12SJohn Marino 
1323ff40c12SJohn Marino 	if (len > WPS_MAX_VENDOR_EXT_LEN) {
1333ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
1343ff40c12SJohn Marino 			   len);
1353ff40c12SJohn Marino 		return -1;
1363ff40c12SJohn Marino 	}
1373ff40c12SJohn Marino 
1383ff40c12SJohn Marino 	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
1393ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
1403ff40c12SJohn Marino 			   "attribute (max %d vendor extensions)",
1413ff40c12SJohn Marino 			   MAX_WPS_PARSE_VENDOR_EXT);
1423ff40c12SJohn Marino 		return -1;
1433ff40c12SJohn Marino 	}
1443ff40c12SJohn Marino 	attr->vendor_ext[attr->num_vendor_ext] = pos;
1453ff40c12SJohn Marino 	attr->vendor_ext_len[attr->num_vendor_ext] = len;
1463ff40c12SJohn Marino 	attr->num_vendor_ext++;
1473ff40c12SJohn Marino 
1483ff40c12SJohn Marino 	return 0;
1493ff40c12SJohn Marino }
1506d49e1aeSJan Lentfer 
1516d49e1aeSJan Lentfer 
wps_set_attr(struct wps_parse_attr * attr,u16 type,const u8 * pos,u16 len)1526d49e1aeSJan Lentfer static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
1536d49e1aeSJan Lentfer 			const u8 *pos, u16 len)
1546d49e1aeSJan Lentfer {
1556d49e1aeSJan Lentfer 	switch (type) {
1566d49e1aeSJan Lentfer 	case ATTR_VERSION:
1576d49e1aeSJan Lentfer 		if (len != 1) {
1586d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
1596d49e1aeSJan Lentfer 				   len);
1606d49e1aeSJan Lentfer 			return -1;
1616d49e1aeSJan Lentfer 		}
1626d49e1aeSJan Lentfer 		attr->version = pos;
1636d49e1aeSJan Lentfer 		break;
1646d49e1aeSJan Lentfer 	case ATTR_MSG_TYPE:
1656d49e1aeSJan Lentfer 		if (len != 1) {
1666d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
1676d49e1aeSJan Lentfer 				   "length %u", len);
1686d49e1aeSJan Lentfer 			return -1;
1696d49e1aeSJan Lentfer 		}
1706d49e1aeSJan Lentfer 		attr->msg_type = pos;
1716d49e1aeSJan Lentfer 		break;
1726d49e1aeSJan Lentfer 	case ATTR_ENROLLEE_NONCE:
1736d49e1aeSJan Lentfer 		if (len != WPS_NONCE_LEN) {
1746d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
1756d49e1aeSJan Lentfer 				   "length %u", len);
1766d49e1aeSJan Lentfer 			return -1;
1776d49e1aeSJan Lentfer 		}
1786d49e1aeSJan Lentfer 		attr->enrollee_nonce = pos;
1796d49e1aeSJan Lentfer 		break;
1806d49e1aeSJan Lentfer 	case ATTR_REGISTRAR_NONCE:
1816d49e1aeSJan Lentfer 		if (len != WPS_NONCE_LEN) {
1826d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
1836d49e1aeSJan Lentfer 				   "length %u", len);
1846d49e1aeSJan Lentfer 			return -1;
1856d49e1aeSJan Lentfer 		}
1866d49e1aeSJan Lentfer 		attr->registrar_nonce = pos;
1876d49e1aeSJan Lentfer 		break;
1886d49e1aeSJan Lentfer 	case ATTR_UUID_E:
1896d49e1aeSJan Lentfer 		if (len != WPS_UUID_LEN) {
1906d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
1916d49e1aeSJan Lentfer 				   len);
1926d49e1aeSJan Lentfer 			return -1;
1936d49e1aeSJan Lentfer 		}
1946d49e1aeSJan Lentfer 		attr->uuid_e = pos;
1956d49e1aeSJan Lentfer 		break;
1966d49e1aeSJan Lentfer 	case ATTR_UUID_R:
1976d49e1aeSJan Lentfer 		if (len != WPS_UUID_LEN) {
1986d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
1996d49e1aeSJan Lentfer 				   len);
2006d49e1aeSJan Lentfer 			return -1;
2016d49e1aeSJan Lentfer 		}
2026d49e1aeSJan Lentfer 		attr->uuid_r = pos;
2036d49e1aeSJan Lentfer 		break;
2046d49e1aeSJan Lentfer 	case ATTR_AUTH_TYPE_FLAGS:
2056d49e1aeSJan Lentfer 		if (len != 2) {
2066d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
2076d49e1aeSJan Lentfer 				   "Type Flags length %u", len);
2086d49e1aeSJan Lentfer 			return -1;
2096d49e1aeSJan Lentfer 		}
2106d49e1aeSJan Lentfer 		attr->auth_type_flags = pos;
2116d49e1aeSJan Lentfer 		break;
2126d49e1aeSJan Lentfer 	case ATTR_ENCR_TYPE_FLAGS:
2136d49e1aeSJan Lentfer 		if (len != 2) {
2146d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
2156d49e1aeSJan Lentfer 				   "Flags length %u", len);
2166d49e1aeSJan Lentfer 			return -1;
2176d49e1aeSJan Lentfer 		}
2186d49e1aeSJan Lentfer 		attr->encr_type_flags = pos;
2196d49e1aeSJan Lentfer 		break;
2206d49e1aeSJan Lentfer 	case ATTR_CONN_TYPE_FLAGS:
2216d49e1aeSJan Lentfer 		if (len != 1) {
2226d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
2236d49e1aeSJan Lentfer 				   "Flags length %u", len);
2246d49e1aeSJan Lentfer 			return -1;
2256d49e1aeSJan Lentfer 		}
2266d49e1aeSJan Lentfer 		attr->conn_type_flags = pos;
2276d49e1aeSJan Lentfer 		break;
2286d49e1aeSJan Lentfer 	case ATTR_CONFIG_METHODS:
2296d49e1aeSJan Lentfer 		if (len != 2) {
2306d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
2316d49e1aeSJan Lentfer 				   "length %u", len);
2326d49e1aeSJan Lentfer 			return -1;
2336d49e1aeSJan Lentfer 		}
2346d49e1aeSJan Lentfer 		attr->config_methods = pos;
2356d49e1aeSJan Lentfer 		break;
2366d49e1aeSJan Lentfer 	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
2376d49e1aeSJan Lentfer 		if (len != 2) {
2386d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
2396d49e1aeSJan Lentfer 				   "Registrar Config Methods length %u", len);
2406d49e1aeSJan Lentfer 			return -1;
2416d49e1aeSJan Lentfer 		}
2426d49e1aeSJan Lentfer 		attr->sel_reg_config_methods = pos;
2436d49e1aeSJan Lentfer 		break;
2446d49e1aeSJan Lentfer 	case ATTR_PRIMARY_DEV_TYPE:
2453ff40c12SJohn Marino 		if (len != WPS_DEV_TYPE_LEN) {
2466d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
2476d49e1aeSJan Lentfer 				   "Type length %u", len);
2486d49e1aeSJan Lentfer 			return -1;
2496d49e1aeSJan Lentfer 		}
2506d49e1aeSJan Lentfer 		attr->primary_dev_type = pos;
2516d49e1aeSJan Lentfer 		break;
2526d49e1aeSJan Lentfer 	case ATTR_RF_BANDS:
2536d49e1aeSJan Lentfer 		if (len != 1) {
2546d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
2556d49e1aeSJan Lentfer 				   "%u", len);
2566d49e1aeSJan Lentfer 			return -1;
2576d49e1aeSJan Lentfer 		}
2586d49e1aeSJan Lentfer 		attr->rf_bands = pos;
2596d49e1aeSJan Lentfer 		break;
2606d49e1aeSJan Lentfer 	case ATTR_ASSOC_STATE:
2616d49e1aeSJan Lentfer 		if (len != 2) {
2626d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
2636d49e1aeSJan Lentfer 				   "length %u", len);
2646d49e1aeSJan Lentfer 			return -1;
2656d49e1aeSJan Lentfer 		}
2666d49e1aeSJan Lentfer 		attr->assoc_state = pos;
2676d49e1aeSJan Lentfer 		break;
2686d49e1aeSJan Lentfer 	case ATTR_CONFIG_ERROR:
2696d49e1aeSJan Lentfer 		if (len != 2) {
2706d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
2716d49e1aeSJan Lentfer 				   "Error length %u", len);
2726d49e1aeSJan Lentfer 			return -1;
2736d49e1aeSJan Lentfer 		}
2746d49e1aeSJan Lentfer 		attr->config_error = pos;
2756d49e1aeSJan Lentfer 		break;
2766d49e1aeSJan Lentfer 	case ATTR_DEV_PASSWORD_ID:
2776d49e1aeSJan Lentfer 		if (len != 2) {
2786d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
2796d49e1aeSJan Lentfer 				   "ID length %u", len);
2806d49e1aeSJan Lentfer 			return -1;
2816d49e1aeSJan Lentfer 		}
2826d49e1aeSJan Lentfer 		attr->dev_password_id = pos;
2836d49e1aeSJan Lentfer 		break;
2843ff40c12SJohn Marino 	case ATTR_OOB_DEVICE_PASSWORD:
2853ff40c12SJohn Marino 		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
2863ff40c12SJohn Marino 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
2873ff40c12SJohn Marino 		    WPS_OOB_DEVICE_PASSWORD_LEN ||
2883ff40c12SJohn Marino 		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
2893ff40c12SJohn Marino 		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
2903ff40c12SJohn Marino 		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
2913ff40c12SJohn Marino 		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
2923ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
2933ff40c12SJohn Marino 				   "Password length %u", len);
2943ff40c12SJohn Marino 			return -1;
2953ff40c12SJohn Marino 		}
2963ff40c12SJohn Marino 		attr->oob_dev_password = pos;
2973ff40c12SJohn Marino 		attr->oob_dev_password_len = len;
2983ff40c12SJohn Marino 		break;
2996d49e1aeSJan Lentfer 	case ATTR_OS_VERSION:
3006d49e1aeSJan Lentfer 		if (len != 4) {
3016d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
3026d49e1aeSJan Lentfer 				   "%u", len);
3036d49e1aeSJan Lentfer 			return -1;
3046d49e1aeSJan Lentfer 		}
3056d49e1aeSJan Lentfer 		attr->os_version = pos;
3066d49e1aeSJan Lentfer 		break;
3076d49e1aeSJan Lentfer 	case ATTR_WPS_STATE:
3086d49e1aeSJan Lentfer 		if (len != 1) {
3096d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
3106d49e1aeSJan Lentfer 				   "Setup State length %u", len);
3116d49e1aeSJan Lentfer 			return -1;
3126d49e1aeSJan Lentfer 		}
3136d49e1aeSJan Lentfer 		attr->wps_state = pos;
3146d49e1aeSJan Lentfer 		break;
3156d49e1aeSJan Lentfer 	case ATTR_AUTHENTICATOR:
3166d49e1aeSJan Lentfer 		if (len != WPS_AUTHENTICATOR_LEN) {
3176d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
3186d49e1aeSJan Lentfer 				   "length %u", len);
3196d49e1aeSJan Lentfer 			return -1;
3206d49e1aeSJan Lentfer 		}
3216d49e1aeSJan Lentfer 		attr->authenticator = pos;
3226d49e1aeSJan Lentfer 		break;
3236d49e1aeSJan Lentfer 	case ATTR_R_HASH1:
3246d49e1aeSJan Lentfer 		if (len != WPS_HASH_LEN) {
3256d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
3266d49e1aeSJan Lentfer 				   len);
3276d49e1aeSJan Lentfer 			return -1;
3286d49e1aeSJan Lentfer 		}
3296d49e1aeSJan Lentfer 		attr->r_hash1 = pos;
3306d49e1aeSJan Lentfer 		break;
3316d49e1aeSJan Lentfer 	case ATTR_R_HASH2:
3326d49e1aeSJan Lentfer 		if (len != WPS_HASH_LEN) {
3336d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
3346d49e1aeSJan Lentfer 				   len);
3356d49e1aeSJan Lentfer 			return -1;
3366d49e1aeSJan Lentfer 		}
3376d49e1aeSJan Lentfer 		attr->r_hash2 = pos;
3386d49e1aeSJan Lentfer 		break;
3396d49e1aeSJan Lentfer 	case ATTR_E_HASH1:
3406d49e1aeSJan Lentfer 		if (len != WPS_HASH_LEN) {
3416d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
3426d49e1aeSJan Lentfer 				   len);
3436d49e1aeSJan Lentfer 			return -1;
3446d49e1aeSJan Lentfer 		}
3456d49e1aeSJan Lentfer 		attr->e_hash1 = pos;
3466d49e1aeSJan Lentfer 		break;
3476d49e1aeSJan Lentfer 	case ATTR_E_HASH2:
3486d49e1aeSJan Lentfer 		if (len != WPS_HASH_LEN) {
3496d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
3506d49e1aeSJan Lentfer 				   len);
3516d49e1aeSJan Lentfer 			return -1;
3526d49e1aeSJan Lentfer 		}
3536d49e1aeSJan Lentfer 		attr->e_hash2 = pos;
3546d49e1aeSJan Lentfer 		break;
3556d49e1aeSJan Lentfer 	case ATTR_R_SNONCE1:
3566d49e1aeSJan Lentfer 		if (len != WPS_SECRET_NONCE_LEN) {
3576d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
3586d49e1aeSJan Lentfer 				   "%u", len);
3596d49e1aeSJan Lentfer 			return -1;
3606d49e1aeSJan Lentfer 		}
3616d49e1aeSJan Lentfer 		attr->r_snonce1 = pos;
3626d49e1aeSJan Lentfer 		break;
3636d49e1aeSJan Lentfer 	case ATTR_R_SNONCE2:
3646d49e1aeSJan Lentfer 		if (len != WPS_SECRET_NONCE_LEN) {
3656d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
3666d49e1aeSJan Lentfer 				   "%u", len);
3676d49e1aeSJan Lentfer 			return -1;
3686d49e1aeSJan Lentfer 		}
3696d49e1aeSJan Lentfer 		attr->r_snonce2 = pos;
3706d49e1aeSJan Lentfer 		break;
3716d49e1aeSJan Lentfer 	case ATTR_E_SNONCE1:
3726d49e1aeSJan Lentfer 		if (len != WPS_SECRET_NONCE_LEN) {
3736d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
3746d49e1aeSJan Lentfer 				   "%u", len);
3756d49e1aeSJan Lentfer 			return -1;
3766d49e1aeSJan Lentfer 		}
3776d49e1aeSJan Lentfer 		attr->e_snonce1 = pos;
3786d49e1aeSJan Lentfer 		break;
3796d49e1aeSJan Lentfer 	case ATTR_E_SNONCE2:
3806d49e1aeSJan Lentfer 		if (len != WPS_SECRET_NONCE_LEN) {
3816d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
3826d49e1aeSJan Lentfer 				   "%u", len);
3836d49e1aeSJan Lentfer 			return -1;
3846d49e1aeSJan Lentfer 		}
3856d49e1aeSJan Lentfer 		attr->e_snonce2 = pos;
3866d49e1aeSJan Lentfer 		break;
3876d49e1aeSJan Lentfer 	case ATTR_KEY_WRAP_AUTH:
3886d49e1aeSJan Lentfer 		if (len != WPS_KWA_LEN) {
3896d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
3906d49e1aeSJan Lentfer 				   "Authenticator length %u", len);
3916d49e1aeSJan Lentfer 			return -1;
3926d49e1aeSJan Lentfer 		}
3936d49e1aeSJan Lentfer 		attr->key_wrap_auth = pos;
3946d49e1aeSJan Lentfer 		break;
3956d49e1aeSJan Lentfer 	case ATTR_AUTH_TYPE:
3966d49e1aeSJan Lentfer 		if (len != 2) {
3976d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
3986d49e1aeSJan Lentfer 				   "Type length %u", len);
3996d49e1aeSJan Lentfer 			return -1;
4006d49e1aeSJan Lentfer 		}
4016d49e1aeSJan Lentfer 		attr->auth_type = pos;
4026d49e1aeSJan Lentfer 		break;
4036d49e1aeSJan Lentfer 	case ATTR_ENCR_TYPE:
4046d49e1aeSJan Lentfer 		if (len != 2) {
4056d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
4066d49e1aeSJan Lentfer 				   "Type length %u", len);
4076d49e1aeSJan Lentfer 			return -1;
4086d49e1aeSJan Lentfer 		}
4096d49e1aeSJan Lentfer 		attr->encr_type = pos;
4106d49e1aeSJan Lentfer 		break;
4116d49e1aeSJan Lentfer 	case ATTR_NETWORK_INDEX:
4126d49e1aeSJan Lentfer 		if (len != 1) {
4136d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
4146d49e1aeSJan Lentfer 				   "length %u", len);
4156d49e1aeSJan Lentfer 			return -1;
4166d49e1aeSJan Lentfer 		}
4176d49e1aeSJan Lentfer 		attr->network_idx = pos;
4186d49e1aeSJan Lentfer 		break;
4196d49e1aeSJan Lentfer 	case ATTR_NETWORK_KEY_INDEX:
4206d49e1aeSJan Lentfer 		if (len != 1) {
4216d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
4226d49e1aeSJan Lentfer 				   "length %u", len);
4236d49e1aeSJan Lentfer 			return -1;
4246d49e1aeSJan Lentfer 		}
4256d49e1aeSJan Lentfer 		attr->network_key_idx = pos;
4266d49e1aeSJan Lentfer 		break;
4276d49e1aeSJan Lentfer 	case ATTR_MAC_ADDR:
4286d49e1aeSJan Lentfer 		if (len != ETH_ALEN) {
4296d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
4306d49e1aeSJan Lentfer 				   "length %u", len);
4316d49e1aeSJan Lentfer 			return -1;
4326d49e1aeSJan Lentfer 		}
4336d49e1aeSJan Lentfer 		attr->mac_addr = pos;
4346d49e1aeSJan Lentfer 		break;
4356d49e1aeSJan Lentfer 	case ATTR_SELECTED_REGISTRAR:
4366d49e1aeSJan Lentfer 		if (len != 1) {
4376d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
4386d49e1aeSJan Lentfer 				   " length %u", len);
4396d49e1aeSJan Lentfer 			return -1;
4406d49e1aeSJan Lentfer 		}
4416d49e1aeSJan Lentfer 		attr->selected_registrar = pos;
4426d49e1aeSJan Lentfer 		break;
4436d49e1aeSJan Lentfer 	case ATTR_REQUEST_TYPE:
4446d49e1aeSJan Lentfer 		if (len != 1) {
4456d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
4466d49e1aeSJan Lentfer 				   "length %u", len);
4476d49e1aeSJan Lentfer 			return -1;
4486d49e1aeSJan Lentfer 		}
4496d49e1aeSJan Lentfer 		attr->request_type = pos;
4506d49e1aeSJan Lentfer 		break;
4516d49e1aeSJan Lentfer 	case ATTR_RESPONSE_TYPE:
4526d49e1aeSJan Lentfer 		if (len != 1) {
4536d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
4546d49e1aeSJan Lentfer 				   "length %u", len);
4556d49e1aeSJan Lentfer 			return -1;
4566d49e1aeSJan Lentfer 		}
4573ff40c12SJohn Marino 		attr->response_type = pos;
4586d49e1aeSJan Lentfer 		break;
4596d49e1aeSJan Lentfer 	case ATTR_MANUFACTURER:
4606d49e1aeSJan Lentfer 		attr->manufacturer = pos;
461*a1157835SDaniel Fojt 		if (len > WPS_MANUFACTURER_MAX_LEN)
462*a1157835SDaniel Fojt 			attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
463*a1157835SDaniel Fojt 		else
4646d49e1aeSJan Lentfer 			attr->manufacturer_len = len;
4656d49e1aeSJan Lentfer 		break;
4666d49e1aeSJan Lentfer 	case ATTR_MODEL_NAME:
4676d49e1aeSJan Lentfer 		attr->model_name = pos;
468*a1157835SDaniel Fojt 		if (len > WPS_MODEL_NAME_MAX_LEN)
469*a1157835SDaniel Fojt 			attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
470*a1157835SDaniel Fojt 		else
4716d49e1aeSJan Lentfer 			attr->model_name_len = len;
4726d49e1aeSJan Lentfer 		break;
4736d49e1aeSJan Lentfer 	case ATTR_MODEL_NUMBER:
4746d49e1aeSJan Lentfer 		attr->model_number = pos;
475*a1157835SDaniel Fojt 		if (len > WPS_MODEL_NUMBER_MAX_LEN)
476*a1157835SDaniel Fojt 			attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
477*a1157835SDaniel Fojt 		else
4786d49e1aeSJan Lentfer 			attr->model_number_len = len;
4796d49e1aeSJan Lentfer 		break;
4806d49e1aeSJan Lentfer 	case ATTR_SERIAL_NUMBER:
4816d49e1aeSJan Lentfer 		attr->serial_number = pos;
482*a1157835SDaniel Fojt 		if (len > WPS_SERIAL_NUMBER_MAX_LEN)
483*a1157835SDaniel Fojt 			attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
484*a1157835SDaniel Fojt 		else
4856d49e1aeSJan Lentfer 			attr->serial_number_len = len;
4866d49e1aeSJan Lentfer 		break;
4876d49e1aeSJan Lentfer 	case ATTR_DEV_NAME:
488*a1157835SDaniel Fojt 		if (len > WPS_DEV_NAME_MAX_LEN) {
489*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
490*a1157835SDaniel Fojt 				   "WPS: Ignore too long Device Name (len=%u)",
491*a1157835SDaniel Fojt 				   len);
492*a1157835SDaniel Fojt 			break;
493*a1157835SDaniel Fojt 		}
4946d49e1aeSJan Lentfer 		attr->dev_name = pos;
4956d49e1aeSJan Lentfer 		attr->dev_name_len = len;
4966d49e1aeSJan Lentfer 		break;
4976d49e1aeSJan Lentfer 	case ATTR_PUBLIC_KEY:
498*a1157835SDaniel Fojt 		/*
499*a1157835SDaniel Fojt 		 * The Public Key attribute is supposed to be exactly 192 bytes
500*a1157835SDaniel Fojt 		 * in length. Allow couple of bytes shorter one to try to
501*a1157835SDaniel Fojt 		 * interoperate with implementations that do not use proper
502*a1157835SDaniel Fojt 		 * zero-padding.
503*a1157835SDaniel Fojt 		 */
504*a1157835SDaniel Fojt 		if (len < 190 || len > 192) {
505*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
506*a1157835SDaniel Fojt 				   "WPS: Ignore Public Key with unexpected length %u",
507*a1157835SDaniel Fojt 				   len);
508*a1157835SDaniel Fojt 			break;
509*a1157835SDaniel Fojt 		}
5106d49e1aeSJan Lentfer 		attr->public_key = pos;
5116d49e1aeSJan Lentfer 		attr->public_key_len = len;
5126d49e1aeSJan Lentfer 		break;
5136d49e1aeSJan Lentfer 	case ATTR_ENCR_SETTINGS:
5146d49e1aeSJan Lentfer 		attr->encr_settings = pos;
5156d49e1aeSJan Lentfer 		attr->encr_settings_len = len;
5166d49e1aeSJan Lentfer 		break;
5176d49e1aeSJan Lentfer 	case ATTR_CRED:
5186d49e1aeSJan Lentfer 		if (attr->num_cred >= MAX_CRED_COUNT) {
5196d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
5206d49e1aeSJan Lentfer 				   "attribute (max %d credentials)",
5216d49e1aeSJan Lentfer 				   MAX_CRED_COUNT);
5226d49e1aeSJan Lentfer 			break;
5236d49e1aeSJan Lentfer 		}
5246d49e1aeSJan Lentfer 		attr->cred[attr->num_cred] = pos;
5256d49e1aeSJan Lentfer 		attr->cred_len[attr->num_cred] = len;
5266d49e1aeSJan Lentfer 		attr->num_cred++;
5276d49e1aeSJan Lentfer 		break;
5286d49e1aeSJan Lentfer 	case ATTR_SSID:
529*a1157835SDaniel Fojt 		if (len > SSID_MAX_LEN) {
530*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
531*a1157835SDaniel Fojt 				   "WPS: Ignore too long SSID (len=%u)", len);
532*a1157835SDaniel Fojt 			break;
533*a1157835SDaniel Fojt 		}
5346d49e1aeSJan Lentfer 		attr->ssid = pos;
5356d49e1aeSJan Lentfer 		attr->ssid_len = len;
5366d49e1aeSJan Lentfer 		break;
5376d49e1aeSJan Lentfer 	case ATTR_NETWORK_KEY:
5386d49e1aeSJan Lentfer 		attr->network_key = pos;
5396d49e1aeSJan Lentfer 		attr->network_key_len = len;
5406d49e1aeSJan Lentfer 		break;
5416d49e1aeSJan Lentfer 	case ATTR_AP_SETUP_LOCKED:
5426d49e1aeSJan Lentfer 		if (len != 1) {
5436d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
5446d49e1aeSJan Lentfer 				   "length %u", len);
5456d49e1aeSJan Lentfer 			return -1;
5466d49e1aeSJan Lentfer 		}
5476d49e1aeSJan Lentfer 		attr->ap_setup_locked = pos;
5486d49e1aeSJan Lentfer 		break;
5493ff40c12SJohn Marino 	case ATTR_REQUESTED_DEV_TYPE:
5503ff40c12SJohn Marino 		if (len != WPS_DEV_TYPE_LEN) {
5513ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
5523ff40c12SJohn Marino 				   "Type length %u", len);
5533ff40c12SJohn Marino 			return -1;
5543ff40c12SJohn Marino 		}
5553ff40c12SJohn Marino 		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
5563ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
5573ff40c12SJohn Marino 				   "Type attribute (max %u types)",
5583ff40c12SJohn Marino 				   MAX_REQ_DEV_TYPE_COUNT);
5593ff40c12SJohn Marino 			break;
5603ff40c12SJohn Marino 		}
5613ff40c12SJohn Marino 		attr->req_dev_type[attr->num_req_dev_type] = pos;
5623ff40c12SJohn Marino 		attr->num_req_dev_type++;
5633ff40c12SJohn Marino 		break;
5643ff40c12SJohn Marino 	case ATTR_SECONDARY_DEV_TYPE_LIST:
5653ff40c12SJohn Marino 		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
5663ff40c12SJohn Marino 		    (len % WPS_DEV_TYPE_LEN) > 0) {
5673ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
5683ff40c12SJohn Marino 				   "Type length %u", len);
5693ff40c12SJohn Marino 			return -1;
5703ff40c12SJohn Marino 		}
5713ff40c12SJohn Marino 		attr->sec_dev_type_list = pos;
5723ff40c12SJohn Marino 		attr->sec_dev_type_list_len = len;
5733ff40c12SJohn Marino 		break;
5743ff40c12SJohn Marino 	case ATTR_VENDOR_EXT:
5753ff40c12SJohn Marino 		if (wps_parse_vendor_ext(attr, pos, len) < 0)
5763ff40c12SJohn Marino 			return -1;
5773ff40c12SJohn Marino 		break;
5783ff40c12SJohn Marino 	case ATTR_AP_CHANNEL:
5793ff40c12SJohn Marino 		if (len != 2) {
5803ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
5813ff40c12SJohn Marino 				   "length %u", len);
5823ff40c12SJohn Marino 			return -1;
5833ff40c12SJohn Marino 		}
5843ff40c12SJohn Marino 		attr->ap_channel = pos;
5853ff40c12SJohn Marino 		break;
5866d49e1aeSJan Lentfer 	default:
5876d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
5886d49e1aeSJan Lentfer 			   "len=%u", type, len);
5896d49e1aeSJan Lentfer 		break;
5906d49e1aeSJan Lentfer 	}
5916d49e1aeSJan Lentfer 
5926d49e1aeSJan Lentfer 	return 0;
5936d49e1aeSJan Lentfer }
5946d49e1aeSJan Lentfer 
5956d49e1aeSJan Lentfer 
wps_parse_msg(const struct wpabuf * msg,struct wps_parse_attr * attr)5966d49e1aeSJan Lentfer int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
5976d49e1aeSJan Lentfer {
5986d49e1aeSJan Lentfer 	const u8 *pos, *end;
5996d49e1aeSJan Lentfer 	u16 type, len;
6003ff40c12SJohn Marino #ifdef WPS_WORKAROUNDS
6013ff40c12SJohn Marino 	u16 prev_type = 0;
6023ff40c12SJohn Marino #endif /* WPS_WORKAROUNDS */
6036d49e1aeSJan Lentfer 
6046d49e1aeSJan Lentfer 	os_memset(attr, 0, sizeof(*attr));
6056d49e1aeSJan Lentfer 	pos = wpabuf_head(msg);
6066d49e1aeSJan Lentfer 	end = pos + wpabuf_len(msg);
6076d49e1aeSJan Lentfer 
6086d49e1aeSJan Lentfer 	while (pos < end) {
6096d49e1aeSJan Lentfer 		if (end - pos < 4) {
6106d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
6116d49e1aeSJan Lentfer 				   "%lu bytes remaining",
6126d49e1aeSJan Lentfer 				   (unsigned long) (end - pos));
6136d49e1aeSJan Lentfer 			return -1;
6146d49e1aeSJan Lentfer 		}
6156d49e1aeSJan Lentfer 
6166d49e1aeSJan Lentfer 		type = WPA_GET_BE16(pos);
6176d49e1aeSJan Lentfer 		pos += 2;
6186d49e1aeSJan Lentfer 		len = WPA_GET_BE16(pos);
6196d49e1aeSJan Lentfer 		pos += 2;
6203ff40c12SJohn Marino 		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
6216d49e1aeSJan Lentfer 			   type, len);
6226d49e1aeSJan Lentfer 		if (len > end - pos) {
6236d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
6243ff40c12SJohn Marino 			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
6253ff40c12SJohn Marino #ifdef WPS_WORKAROUNDS
6263ff40c12SJohn Marino 			/*
6273ff40c12SJohn Marino 			 * Some deployed APs seem to have a bug in encoding of
6283ff40c12SJohn Marino 			 * Network Key attribute in the Credential attribute
6293ff40c12SJohn Marino 			 * where they add an extra octet after the Network Key
6303ff40c12SJohn Marino 			 * attribute at least when open network is being
6313ff40c12SJohn Marino 			 * provisioned.
6323ff40c12SJohn Marino 			 */
6333ff40c12SJohn Marino 			if ((type & 0xff00) != 0x1000 &&
6343ff40c12SJohn Marino 			    prev_type == ATTR_NETWORK_KEY) {
6353ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
6363ff40c12SJohn Marino 					   "to skip unexpected octet after "
6373ff40c12SJohn Marino 					   "Network Key");
6383ff40c12SJohn Marino 				pos -= 3;
6393ff40c12SJohn Marino 				continue;
6403ff40c12SJohn Marino 			}
6413ff40c12SJohn Marino #endif /* WPS_WORKAROUNDS */
6426d49e1aeSJan Lentfer 			return -1;
6436d49e1aeSJan Lentfer 		}
6446d49e1aeSJan Lentfer 
6453ff40c12SJohn Marino #ifdef WPS_WORKAROUNDS
6463ff40c12SJohn Marino 		if (type == 0 && len == 0) {
6473ff40c12SJohn Marino 			/*
6483ff40c12SJohn Marino 			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
6493ff40c12SJohn Marino 			 * end of M1. Skip those to avoid interop issues.
6503ff40c12SJohn Marino 			 */
6513ff40c12SJohn Marino 			int i;
6523ff40c12SJohn Marino 			for (i = 0; i < end - pos; i++) {
6533ff40c12SJohn Marino 				if (pos[i])
6543ff40c12SJohn Marino 					break;
6553ff40c12SJohn Marino 			}
6563ff40c12SJohn Marino 			if (i == end - pos) {
6573ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
6583ff40c12SJohn Marino 					   "unexpected message padding");
6593ff40c12SJohn Marino 				break;
6603ff40c12SJohn Marino 			}
6613ff40c12SJohn Marino 		}
6623ff40c12SJohn Marino #endif /* WPS_WORKAROUNDS */
6633ff40c12SJohn Marino 
6646d49e1aeSJan Lentfer 		if (wps_set_attr(attr, type, pos, len) < 0)
6656d49e1aeSJan Lentfer 			return -1;
6666d49e1aeSJan Lentfer 
6673ff40c12SJohn Marino #ifdef WPS_WORKAROUNDS
6683ff40c12SJohn Marino 		prev_type = type;
6693ff40c12SJohn Marino #endif /* WPS_WORKAROUNDS */
6706d49e1aeSJan Lentfer 		pos += len;
6716d49e1aeSJan Lentfer 	}
6726d49e1aeSJan Lentfer 
6736d49e1aeSJan Lentfer 	return 0;
6746d49e1aeSJan Lentfer }
675