xref: /freebsd/contrib/wpa/src/wps/wps_attr_parse.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * Wi-Fi Protected Setup - attribute parsing
339beb93cSSam Leffler  * Copyright (c) 2008, 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"
12f05cddf9SRui Paulo #include "wps_defs.h"
13f05cddf9SRui Paulo #include "wps_attr_parse.h"
1439beb93cSSam Leffler 
15f05cddf9SRui Paulo #ifndef CONFIG_WPS_STRICT
16e28a4053SRui Paulo #define WPS_WORKAROUNDS
17f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
18f05cddf9SRui Paulo 
19f05cddf9SRui Paulo 
wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr * attr,u8 id,u8 len,const u8 * pos)20f05cddf9SRui Paulo static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
21f05cddf9SRui Paulo 					  u8 id, u8 len, const u8 *pos)
22f05cddf9SRui Paulo {
23f05cddf9SRui Paulo 	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
24f05cddf9SRui Paulo 		   id, len);
25f05cddf9SRui Paulo 	switch (id) {
26f05cddf9SRui Paulo 	case WFA_ELEM_VERSION2:
27f05cddf9SRui Paulo 		if (len != 1) {
28f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
29f05cddf9SRui Paulo 				   "%u", len);
30f05cddf9SRui Paulo 			return -1;
31f05cddf9SRui Paulo 		}
32f05cddf9SRui Paulo 		attr->version2 = pos;
33f05cddf9SRui Paulo 		break;
34f05cddf9SRui Paulo 	case WFA_ELEM_AUTHORIZEDMACS:
35f05cddf9SRui Paulo 		attr->authorized_macs = pos;
36f05cddf9SRui Paulo 		attr->authorized_macs_len = len;
37f05cddf9SRui Paulo 		break;
38f05cddf9SRui Paulo 	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
39f05cddf9SRui Paulo 		if (len != 1) {
40f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
41f05cddf9SRui Paulo 				   "Shareable length %u", len);
42f05cddf9SRui Paulo 			return -1;
43f05cddf9SRui Paulo 		}
44f05cddf9SRui Paulo 		attr->network_key_shareable = pos;
45f05cddf9SRui Paulo 		break;
46f05cddf9SRui Paulo 	case WFA_ELEM_REQUEST_TO_ENROLL:
47f05cddf9SRui Paulo 		if (len != 1) {
48f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
49f05cddf9SRui Paulo 				   "length %u", len);
50f05cddf9SRui Paulo 			return -1;
51f05cddf9SRui Paulo 		}
52f05cddf9SRui Paulo 		attr->request_to_enroll = pos;
53f05cddf9SRui Paulo 		break;
54f05cddf9SRui Paulo 	case WFA_ELEM_SETTINGS_DELAY_TIME:
55f05cddf9SRui Paulo 		if (len != 1) {
56f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
57f05cddf9SRui Paulo 				   "Time length %u", len);
58f05cddf9SRui Paulo 			return -1;
59f05cddf9SRui Paulo 		}
60f05cddf9SRui Paulo 		attr->settings_delay_time = pos;
61f05cddf9SRui Paulo 		break;
625b9c547cSRui Paulo 	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
635b9c547cSRui Paulo 		if (len != 2) {
645b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
655b9c547cSRui Paulo 				   len);
665b9c547cSRui Paulo 			return -1;
675b9c547cSRui Paulo 		}
685b9c547cSRui Paulo 		attr->registrar_configuration_methods = pos;
695b9c547cSRui Paulo 		break;
704bc52338SCy Schubert 	case WFA_ELEM_MULTI_AP:
714bc52338SCy Schubert 		if (len != 1) {
724bc52338SCy Schubert 			wpa_printf(MSG_DEBUG,
734bc52338SCy Schubert 				   "WPS: Invalid Multi-AP Extension length %u",
744bc52338SCy Schubert 				   len);
754bc52338SCy Schubert 			return -1;
764bc52338SCy Schubert 		}
774bc52338SCy Schubert 		attr->multi_ap_ext = *pos;
784bc52338SCy Schubert 		wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
794bc52338SCy Schubert 			   attr->multi_ap_ext);
804bc52338SCy Schubert 		break;
81f05cddf9SRui Paulo 	default:
82f05cddf9SRui Paulo 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
83f05cddf9SRui Paulo 			   "Extension subelement %u", id);
84f05cddf9SRui Paulo 		break;
85f05cddf9SRui Paulo 	}
86f05cddf9SRui Paulo 
87f05cddf9SRui Paulo 	return 0;
88f05cddf9SRui Paulo }
89f05cddf9SRui Paulo 
90f05cddf9SRui Paulo 
wps_parse_vendor_ext_wfa(struct wps_parse_attr * attr,const u8 * pos,u16 len)91f05cddf9SRui Paulo static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
92f05cddf9SRui Paulo 				    u16 len)
93f05cddf9SRui Paulo {
94f05cddf9SRui Paulo 	const u8 *end = pos + len;
95f05cddf9SRui Paulo 	u8 id, elen;
96f05cddf9SRui Paulo 
97780fb4a2SCy Schubert 	while (end - pos >= 2) {
98f05cddf9SRui Paulo 		id = *pos++;
99f05cddf9SRui Paulo 		elen = *pos++;
100780fb4a2SCy Schubert 		if (elen > end - pos)
101f05cddf9SRui Paulo 			break;
102f05cddf9SRui Paulo 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
103f05cddf9SRui Paulo 			return -1;
104f05cddf9SRui Paulo 		pos += elen;
105f05cddf9SRui Paulo 	}
106f05cddf9SRui Paulo 
107f05cddf9SRui Paulo 	return 0;
108f05cddf9SRui Paulo }
109f05cddf9SRui Paulo 
110f05cddf9SRui Paulo 
wps_parse_vendor_ext(struct wps_parse_attr * attr,const u8 * pos,u16 len)111f05cddf9SRui Paulo static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
112f05cddf9SRui Paulo 				u16 len)
113f05cddf9SRui Paulo {
114f05cddf9SRui Paulo 	u32 vendor_id;
115f05cddf9SRui Paulo 
116f05cddf9SRui Paulo 	if (len < 3) {
117f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
118f05cddf9SRui Paulo 		return 0;
119f05cddf9SRui Paulo 	}
120f05cddf9SRui Paulo 
121f05cddf9SRui Paulo 	vendor_id = WPA_GET_BE24(pos);
122f05cddf9SRui Paulo 	switch (vendor_id) {
123f05cddf9SRui Paulo 	case WPS_VENDOR_ID_WFA:
124f05cddf9SRui Paulo 		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
125f05cddf9SRui Paulo 	}
126f05cddf9SRui Paulo 
127f05cddf9SRui Paulo 	/* Handle unknown vendor extensions */
128f05cddf9SRui Paulo 
129f05cddf9SRui Paulo 	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
130f05cddf9SRui Paulo 		   vendor_id);
131f05cddf9SRui Paulo 
132f05cddf9SRui Paulo 	if (len > WPS_MAX_VENDOR_EXT_LEN) {
133f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
134f05cddf9SRui Paulo 			   len);
135f05cddf9SRui Paulo 		return -1;
136f05cddf9SRui Paulo 	}
137f05cddf9SRui Paulo 
138f05cddf9SRui Paulo 	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
139f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
140f05cddf9SRui Paulo 			   "attribute (max %d vendor extensions)",
141f05cddf9SRui Paulo 			   MAX_WPS_PARSE_VENDOR_EXT);
142f05cddf9SRui Paulo 		return -1;
143f05cddf9SRui Paulo 	}
144f05cddf9SRui Paulo 	attr->vendor_ext[attr->num_vendor_ext] = pos;
145f05cddf9SRui Paulo 	attr->vendor_ext_len[attr->num_vendor_ext] = len;
146f05cddf9SRui Paulo 	attr->num_vendor_ext++;
147f05cddf9SRui Paulo 
148f05cddf9SRui Paulo 	return 0;
149f05cddf9SRui Paulo }
150e28a4053SRui Paulo 
15139beb93cSSam Leffler 
wps_set_attr(struct wps_parse_attr * attr,u16 type,const u8 * pos,u16 len)15239beb93cSSam Leffler static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
15339beb93cSSam Leffler 			const u8 *pos, u16 len)
15439beb93cSSam Leffler {
15539beb93cSSam Leffler 	switch (type) {
15639beb93cSSam Leffler 	case ATTR_VERSION:
15739beb93cSSam Leffler 		if (len != 1) {
15839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
15939beb93cSSam Leffler 				   len);
16039beb93cSSam Leffler 			return -1;
16139beb93cSSam Leffler 		}
16239beb93cSSam Leffler 		attr->version = pos;
16339beb93cSSam Leffler 		break;
16439beb93cSSam Leffler 	case ATTR_MSG_TYPE:
16539beb93cSSam Leffler 		if (len != 1) {
16639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
16739beb93cSSam Leffler 				   "length %u", len);
16839beb93cSSam Leffler 			return -1;
16939beb93cSSam Leffler 		}
17039beb93cSSam Leffler 		attr->msg_type = pos;
17139beb93cSSam Leffler 		break;
17239beb93cSSam Leffler 	case ATTR_ENROLLEE_NONCE:
17339beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
17439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
17539beb93cSSam Leffler 				   "length %u", len);
17639beb93cSSam Leffler 			return -1;
17739beb93cSSam Leffler 		}
17839beb93cSSam Leffler 		attr->enrollee_nonce = pos;
17939beb93cSSam Leffler 		break;
18039beb93cSSam Leffler 	case ATTR_REGISTRAR_NONCE:
18139beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
18239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
18339beb93cSSam Leffler 				   "length %u", len);
18439beb93cSSam Leffler 			return -1;
18539beb93cSSam Leffler 		}
18639beb93cSSam Leffler 		attr->registrar_nonce = pos;
18739beb93cSSam Leffler 		break;
18839beb93cSSam Leffler 	case ATTR_UUID_E:
18939beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
19039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
19139beb93cSSam Leffler 				   len);
19239beb93cSSam Leffler 			return -1;
19339beb93cSSam Leffler 		}
19439beb93cSSam Leffler 		attr->uuid_e = pos;
19539beb93cSSam Leffler 		break;
19639beb93cSSam Leffler 	case ATTR_UUID_R:
19739beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
19839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
19939beb93cSSam Leffler 				   len);
20039beb93cSSam Leffler 			return -1;
20139beb93cSSam Leffler 		}
20239beb93cSSam Leffler 		attr->uuid_r = pos;
20339beb93cSSam Leffler 		break;
20439beb93cSSam Leffler 	case ATTR_AUTH_TYPE_FLAGS:
20539beb93cSSam Leffler 		if (len != 2) {
20639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
20739beb93cSSam Leffler 				   "Type Flags length %u", len);
20839beb93cSSam Leffler 			return -1;
20939beb93cSSam Leffler 		}
21039beb93cSSam Leffler 		attr->auth_type_flags = pos;
21139beb93cSSam Leffler 		break;
21239beb93cSSam Leffler 	case ATTR_ENCR_TYPE_FLAGS:
21339beb93cSSam Leffler 		if (len != 2) {
21439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
21539beb93cSSam Leffler 				   "Flags length %u", len);
21639beb93cSSam Leffler 			return -1;
21739beb93cSSam Leffler 		}
21839beb93cSSam Leffler 		attr->encr_type_flags = pos;
21939beb93cSSam Leffler 		break;
22039beb93cSSam Leffler 	case ATTR_CONN_TYPE_FLAGS:
22139beb93cSSam Leffler 		if (len != 1) {
22239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
22339beb93cSSam Leffler 				   "Flags length %u", len);
22439beb93cSSam Leffler 			return -1;
22539beb93cSSam Leffler 		}
22639beb93cSSam Leffler 		attr->conn_type_flags = pos;
22739beb93cSSam Leffler 		break;
22839beb93cSSam Leffler 	case ATTR_CONFIG_METHODS:
22939beb93cSSam Leffler 		if (len != 2) {
23039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
23139beb93cSSam Leffler 				   "length %u", len);
23239beb93cSSam Leffler 			return -1;
23339beb93cSSam Leffler 		}
23439beb93cSSam Leffler 		attr->config_methods = pos;
23539beb93cSSam Leffler 		break;
23639beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
23739beb93cSSam Leffler 		if (len != 2) {
23839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
23939beb93cSSam Leffler 				   "Registrar Config Methods length %u", len);
24039beb93cSSam Leffler 			return -1;
24139beb93cSSam Leffler 		}
24239beb93cSSam Leffler 		attr->sel_reg_config_methods = pos;
24339beb93cSSam Leffler 		break;
24439beb93cSSam Leffler 	case ATTR_PRIMARY_DEV_TYPE:
245e28a4053SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
24639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
24739beb93cSSam Leffler 				   "Type length %u", len);
24839beb93cSSam Leffler 			return -1;
24939beb93cSSam Leffler 		}
25039beb93cSSam Leffler 		attr->primary_dev_type = pos;
25139beb93cSSam Leffler 		break;
25239beb93cSSam Leffler 	case ATTR_RF_BANDS:
25339beb93cSSam Leffler 		if (len != 1) {
25439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
25539beb93cSSam Leffler 				   "%u", len);
25639beb93cSSam Leffler 			return -1;
25739beb93cSSam Leffler 		}
25839beb93cSSam Leffler 		attr->rf_bands = pos;
25939beb93cSSam Leffler 		break;
26039beb93cSSam Leffler 	case ATTR_ASSOC_STATE:
26139beb93cSSam Leffler 		if (len != 2) {
26239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
26339beb93cSSam Leffler 				   "length %u", len);
26439beb93cSSam Leffler 			return -1;
26539beb93cSSam Leffler 		}
26639beb93cSSam Leffler 		attr->assoc_state = pos;
26739beb93cSSam Leffler 		break;
26839beb93cSSam Leffler 	case ATTR_CONFIG_ERROR:
26939beb93cSSam Leffler 		if (len != 2) {
27039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
27139beb93cSSam Leffler 				   "Error length %u", len);
27239beb93cSSam Leffler 			return -1;
27339beb93cSSam Leffler 		}
27439beb93cSSam Leffler 		attr->config_error = pos;
27539beb93cSSam Leffler 		break;
27639beb93cSSam Leffler 	case ATTR_DEV_PASSWORD_ID:
27739beb93cSSam Leffler 		if (len != 2) {
27839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
27939beb93cSSam Leffler 				   "ID length %u", len);
28039beb93cSSam Leffler 			return -1;
28139beb93cSSam Leffler 		}
28239beb93cSSam Leffler 		attr->dev_password_id = pos;
28339beb93cSSam Leffler 		break;
284e28a4053SRui Paulo 	case ATTR_OOB_DEVICE_PASSWORD:
2855b9c547cSRui Paulo 		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
286f05cddf9SRui Paulo 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
2875b9c547cSRui Paulo 		    WPS_OOB_DEVICE_PASSWORD_LEN ||
2885b9c547cSRui Paulo 		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
2895b9c547cSRui Paulo 		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
2905b9c547cSRui Paulo 		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
2915b9c547cSRui Paulo 		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
292e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
293e28a4053SRui Paulo 				   "Password length %u", len);
294e28a4053SRui Paulo 			return -1;
295e28a4053SRui Paulo 		}
296e28a4053SRui Paulo 		attr->oob_dev_password = pos;
297f05cddf9SRui Paulo 		attr->oob_dev_password_len = len;
298e28a4053SRui Paulo 		break;
29939beb93cSSam Leffler 	case ATTR_OS_VERSION:
30039beb93cSSam Leffler 		if (len != 4) {
30139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
30239beb93cSSam Leffler 				   "%u", len);
30339beb93cSSam Leffler 			return -1;
30439beb93cSSam Leffler 		}
30539beb93cSSam Leffler 		attr->os_version = pos;
30639beb93cSSam Leffler 		break;
30739beb93cSSam Leffler 	case ATTR_WPS_STATE:
30839beb93cSSam Leffler 		if (len != 1) {
30939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
31039beb93cSSam Leffler 				   "Setup State length %u", len);
31139beb93cSSam Leffler 			return -1;
31239beb93cSSam Leffler 		}
31339beb93cSSam Leffler 		attr->wps_state = pos;
31439beb93cSSam Leffler 		break;
31539beb93cSSam Leffler 	case ATTR_AUTHENTICATOR:
31639beb93cSSam Leffler 		if (len != WPS_AUTHENTICATOR_LEN) {
31739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
31839beb93cSSam Leffler 				   "length %u", len);
31939beb93cSSam Leffler 			return -1;
32039beb93cSSam Leffler 		}
32139beb93cSSam Leffler 		attr->authenticator = pos;
32239beb93cSSam Leffler 		break;
32339beb93cSSam Leffler 	case ATTR_R_HASH1:
32439beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
32539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
32639beb93cSSam Leffler 				   len);
32739beb93cSSam Leffler 			return -1;
32839beb93cSSam Leffler 		}
32939beb93cSSam Leffler 		attr->r_hash1 = pos;
33039beb93cSSam Leffler 		break;
33139beb93cSSam Leffler 	case ATTR_R_HASH2:
33239beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
33339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
33439beb93cSSam Leffler 				   len);
33539beb93cSSam Leffler 			return -1;
33639beb93cSSam Leffler 		}
33739beb93cSSam Leffler 		attr->r_hash2 = pos;
33839beb93cSSam Leffler 		break;
33939beb93cSSam Leffler 	case ATTR_E_HASH1:
34039beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
34139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
34239beb93cSSam Leffler 				   len);
34339beb93cSSam Leffler 			return -1;
34439beb93cSSam Leffler 		}
34539beb93cSSam Leffler 		attr->e_hash1 = pos;
34639beb93cSSam Leffler 		break;
34739beb93cSSam Leffler 	case ATTR_E_HASH2:
34839beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
34939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
35039beb93cSSam Leffler 				   len);
35139beb93cSSam Leffler 			return -1;
35239beb93cSSam Leffler 		}
35339beb93cSSam Leffler 		attr->e_hash2 = pos;
35439beb93cSSam Leffler 		break;
35539beb93cSSam Leffler 	case ATTR_R_SNONCE1:
35639beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
35739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
35839beb93cSSam Leffler 				   "%u", len);
35939beb93cSSam Leffler 			return -1;
36039beb93cSSam Leffler 		}
36139beb93cSSam Leffler 		attr->r_snonce1 = pos;
36239beb93cSSam Leffler 		break;
36339beb93cSSam Leffler 	case ATTR_R_SNONCE2:
36439beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
36539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
36639beb93cSSam Leffler 				   "%u", len);
36739beb93cSSam Leffler 			return -1;
36839beb93cSSam Leffler 		}
36939beb93cSSam Leffler 		attr->r_snonce2 = pos;
37039beb93cSSam Leffler 		break;
37139beb93cSSam Leffler 	case ATTR_E_SNONCE1:
37239beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
37339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
37439beb93cSSam Leffler 				   "%u", len);
37539beb93cSSam Leffler 			return -1;
37639beb93cSSam Leffler 		}
37739beb93cSSam Leffler 		attr->e_snonce1 = pos;
37839beb93cSSam Leffler 		break;
37939beb93cSSam Leffler 	case ATTR_E_SNONCE2:
38039beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
38139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
38239beb93cSSam Leffler 				   "%u", len);
38339beb93cSSam Leffler 			return -1;
38439beb93cSSam Leffler 		}
38539beb93cSSam Leffler 		attr->e_snonce2 = pos;
38639beb93cSSam Leffler 		break;
38739beb93cSSam Leffler 	case ATTR_KEY_WRAP_AUTH:
38839beb93cSSam Leffler 		if (len != WPS_KWA_LEN) {
38939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
39039beb93cSSam Leffler 				   "Authenticator length %u", len);
39139beb93cSSam Leffler 			return -1;
39239beb93cSSam Leffler 		}
39339beb93cSSam Leffler 		attr->key_wrap_auth = pos;
39439beb93cSSam Leffler 		break;
39539beb93cSSam Leffler 	case ATTR_AUTH_TYPE:
39639beb93cSSam Leffler 		if (len != 2) {
39739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
39839beb93cSSam Leffler 				   "Type length %u", len);
39939beb93cSSam Leffler 			return -1;
40039beb93cSSam Leffler 		}
40139beb93cSSam Leffler 		attr->auth_type = pos;
40239beb93cSSam Leffler 		break;
40339beb93cSSam Leffler 	case ATTR_ENCR_TYPE:
40439beb93cSSam Leffler 		if (len != 2) {
40539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
40639beb93cSSam Leffler 				   "Type length %u", len);
40739beb93cSSam Leffler 			return -1;
40839beb93cSSam Leffler 		}
40939beb93cSSam Leffler 		attr->encr_type = pos;
41039beb93cSSam Leffler 		break;
41139beb93cSSam Leffler 	case ATTR_NETWORK_INDEX:
41239beb93cSSam Leffler 		if (len != 1) {
41339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
41439beb93cSSam Leffler 				   "length %u", len);
41539beb93cSSam Leffler 			return -1;
41639beb93cSSam Leffler 		}
41739beb93cSSam Leffler 		attr->network_idx = pos;
41839beb93cSSam Leffler 		break;
41939beb93cSSam Leffler 	case ATTR_NETWORK_KEY_INDEX:
42039beb93cSSam Leffler 		if (len != 1) {
42139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
42239beb93cSSam Leffler 				   "length %u", len);
42339beb93cSSam Leffler 			return -1;
42439beb93cSSam Leffler 		}
42539beb93cSSam Leffler 		attr->network_key_idx = pos;
42639beb93cSSam Leffler 		break;
42739beb93cSSam Leffler 	case ATTR_MAC_ADDR:
42839beb93cSSam Leffler 		if (len != ETH_ALEN) {
42939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
43039beb93cSSam Leffler 				   "length %u", len);
43139beb93cSSam Leffler 			return -1;
43239beb93cSSam Leffler 		}
43339beb93cSSam Leffler 		attr->mac_addr = pos;
43439beb93cSSam Leffler 		break;
43539beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR:
43639beb93cSSam Leffler 		if (len != 1) {
43739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
43839beb93cSSam Leffler 				   " length %u", len);
43939beb93cSSam Leffler 			return -1;
44039beb93cSSam Leffler 		}
44139beb93cSSam Leffler 		attr->selected_registrar = pos;
44239beb93cSSam Leffler 		break;
44339beb93cSSam Leffler 	case ATTR_REQUEST_TYPE:
44439beb93cSSam Leffler 		if (len != 1) {
44539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
44639beb93cSSam Leffler 				   "length %u", len);
44739beb93cSSam Leffler 			return -1;
44839beb93cSSam Leffler 		}
44939beb93cSSam Leffler 		attr->request_type = pos;
45039beb93cSSam Leffler 		break;
45139beb93cSSam Leffler 	case ATTR_RESPONSE_TYPE:
45239beb93cSSam Leffler 		if (len != 1) {
45339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
45439beb93cSSam Leffler 				   "length %u", len);
45539beb93cSSam Leffler 			return -1;
45639beb93cSSam Leffler 		}
457e28a4053SRui Paulo 		attr->response_type = pos;
45839beb93cSSam Leffler 		break;
45939beb93cSSam Leffler 	case ATTR_MANUFACTURER:
46039beb93cSSam Leffler 		attr->manufacturer = pos;
461325151a3SRui Paulo 		if (len > WPS_MANUFACTURER_MAX_LEN)
462325151a3SRui Paulo 			attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
463325151a3SRui Paulo 		else
46439beb93cSSam Leffler 			attr->manufacturer_len = len;
46539beb93cSSam Leffler 		break;
46639beb93cSSam Leffler 	case ATTR_MODEL_NAME:
46739beb93cSSam Leffler 		attr->model_name = pos;
468325151a3SRui Paulo 		if (len > WPS_MODEL_NAME_MAX_LEN)
469325151a3SRui Paulo 			attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
470325151a3SRui Paulo 		else
47139beb93cSSam Leffler 			attr->model_name_len = len;
47239beb93cSSam Leffler 		break;
47339beb93cSSam Leffler 	case ATTR_MODEL_NUMBER:
47439beb93cSSam Leffler 		attr->model_number = pos;
475325151a3SRui Paulo 		if (len > WPS_MODEL_NUMBER_MAX_LEN)
476325151a3SRui Paulo 			attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
477325151a3SRui Paulo 		else
47839beb93cSSam Leffler 			attr->model_number_len = len;
47939beb93cSSam Leffler 		break;
48039beb93cSSam Leffler 	case ATTR_SERIAL_NUMBER:
48139beb93cSSam Leffler 		attr->serial_number = pos;
482325151a3SRui Paulo 		if (len > WPS_SERIAL_NUMBER_MAX_LEN)
483325151a3SRui Paulo 			attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
484325151a3SRui Paulo 		else
48539beb93cSSam Leffler 			attr->serial_number_len = len;
48639beb93cSSam Leffler 		break;
48739beb93cSSam Leffler 	case ATTR_DEV_NAME:
488325151a3SRui Paulo 		if (len > WPS_DEV_NAME_MAX_LEN) {
489325151a3SRui Paulo 			wpa_printf(MSG_DEBUG,
490325151a3SRui Paulo 				   "WPS: Ignore too long Device Name (len=%u)",
491325151a3SRui Paulo 				   len);
492325151a3SRui Paulo 			break;
493325151a3SRui Paulo 		}
49439beb93cSSam Leffler 		attr->dev_name = pos;
49539beb93cSSam Leffler 		attr->dev_name_len = len;
49639beb93cSSam Leffler 		break;
49739beb93cSSam Leffler 	case ATTR_PUBLIC_KEY:
498325151a3SRui Paulo 		/*
499325151a3SRui Paulo 		 * The Public Key attribute is supposed to be exactly 192 bytes
500325151a3SRui Paulo 		 * in length. Allow couple of bytes shorter one to try to
501325151a3SRui Paulo 		 * interoperate with implementations that do not use proper
502325151a3SRui Paulo 		 * zero-padding.
503325151a3SRui Paulo 		 */
504325151a3SRui Paulo 		if (len < 190 || len > 192) {
505325151a3SRui Paulo 			wpa_printf(MSG_DEBUG,
506325151a3SRui Paulo 				   "WPS: Ignore Public Key with unexpected length %u",
507325151a3SRui Paulo 				   len);
508325151a3SRui Paulo 			break;
509325151a3SRui Paulo 		}
51039beb93cSSam Leffler 		attr->public_key = pos;
51139beb93cSSam Leffler 		attr->public_key_len = len;
51239beb93cSSam Leffler 		break;
51339beb93cSSam Leffler 	case ATTR_ENCR_SETTINGS:
51439beb93cSSam Leffler 		attr->encr_settings = pos;
51539beb93cSSam Leffler 		attr->encr_settings_len = len;
51639beb93cSSam Leffler 		break;
51739beb93cSSam Leffler 	case ATTR_CRED:
51839beb93cSSam Leffler 		if (attr->num_cred >= MAX_CRED_COUNT) {
51939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
52039beb93cSSam Leffler 				   "attribute (max %d credentials)",
52139beb93cSSam Leffler 				   MAX_CRED_COUNT);
52239beb93cSSam Leffler 			break;
52339beb93cSSam Leffler 		}
52439beb93cSSam Leffler 		attr->cred[attr->num_cred] = pos;
52539beb93cSSam Leffler 		attr->cred_len[attr->num_cred] = len;
52639beb93cSSam Leffler 		attr->num_cred++;
52739beb93cSSam Leffler 		break;
52839beb93cSSam Leffler 	case ATTR_SSID:
529325151a3SRui Paulo 		if (len > SSID_MAX_LEN) {
530325151a3SRui Paulo 			wpa_printf(MSG_DEBUG,
531325151a3SRui Paulo 				   "WPS: Ignore too long SSID (len=%u)", len);
532325151a3SRui Paulo 			break;
533325151a3SRui Paulo 		}
53439beb93cSSam Leffler 		attr->ssid = pos;
53539beb93cSSam Leffler 		attr->ssid_len = len;
53639beb93cSSam Leffler 		break;
53739beb93cSSam Leffler 	case ATTR_NETWORK_KEY:
53839beb93cSSam Leffler 		attr->network_key = pos;
53939beb93cSSam Leffler 		attr->network_key_len = len;
54039beb93cSSam Leffler 		break;
5413157ba21SRui Paulo 	case ATTR_AP_SETUP_LOCKED:
5423157ba21SRui Paulo 		if (len != 1) {
5433157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
5443157ba21SRui Paulo 				   "length %u", len);
5453157ba21SRui Paulo 			return -1;
5463157ba21SRui Paulo 		}
5473157ba21SRui Paulo 		attr->ap_setup_locked = pos;
5483157ba21SRui Paulo 		break;
549f05cddf9SRui Paulo 	case ATTR_REQUESTED_DEV_TYPE:
550f05cddf9SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
551f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
552f05cddf9SRui Paulo 				   "Type length %u", len);
553f05cddf9SRui Paulo 			return -1;
554f05cddf9SRui Paulo 		}
555f05cddf9SRui Paulo 		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
556f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
557f05cddf9SRui Paulo 				   "Type attribute (max %u types)",
558f05cddf9SRui Paulo 				   MAX_REQ_DEV_TYPE_COUNT);
559f05cddf9SRui Paulo 			break;
560f05cddf9SRui Paulo 		}
561f05cddf9SRui Paulo 		attr->req_dev_type[attr->num_req_dev_type] = pos;
562f05cddf9SRui Paulo 		attr->num_req_dev_type++;
563f05cddf9SRui Paulo 		break;
564f05cddf9SRui Paulo 	case ATTR_SECONDARY_DEV_TYPE_LIST:
565f05cddf9SRui Paulo 		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
566f05cddf9SRui Paulo 		    (len % WPS_DEV_TYPE_LEN) > 0) {
567f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
568f05cddf9SRui Paulo 				   "Type length %u", len);
569f05cddf9SRui Paulo 			return -1;
570f05cddf9SRui Paulo 		}
571f05cddf9SRui Paulo 		attr->sec_dev_type_list = pos;
572f05cddf9SRui Paulo 		attr->sec_dev_type_list_len = len;
573f05cddf9SRui Paulo 		break;
574f05cddf9SRui Paulo 	case ATTR_VENDOR_EXT:
575f05cddf9SRui Paulo 		if (wps_parse_vendor_ext(attr, pos, len) < 0)
576f05cddf9SRui Paulo 			return -1;
577f05cddf9SRui Paulo 		break;
578f05cddf9SRui Paulo 	case ATTR_AP_CHANNEL:
579f05cddf9SRui Paulo 		if (len != 2) {
580f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
581f05cddf9SRui Paulo 				   "length %u", len);
582f05cddf9SRui Paulo 			return -1;
583f05cddf9SRui Paulo 		}
584f05cddf9SRui Paulo 		attr->ap_channel = pos;
585f05cddf9SRui Paulo 		break;
58639beb93cSSam Leffler 	default:
58739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
58839beb93cSSam Leffler 			   "len=%u", type, len);
58939beb93cSSam Leffler 		break;
59039beb93cSSam Leffler 	}
59139beb93cSSam Leffler 
59239beb93cSSam Leffler 	return 0;
59339beb93cSSam Leffler }
59439beb93cSSam Leffler 
59539beb93cSSam Leffler 
wps_parse_msg(const struct wpabuf * msg,struct wps_parse_attr * attr)59639beb93cSSam Leffler int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
59739beb93cSSam Leffler {
59839beb93cSSam Leffler 	const u8 *pos, *end;
59939beb93cSSam Leffler 	u16 type, len;
600f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
601f05cddf9SRui Paulo 	u16 prev_type = 0;
602*a90b9d01SCy Schubert 	size_t last_nonzero = 0;
603*a90b9d01SCy Schubert 	const u8 *start;
604f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
60539beb93cSSam Leffler 
60639beb93cSSam Leffler 	os_memset(attr, 0, sizeof(*attr));
60739beb93cSSam Leffler 	pos = wpabuf_head(msg);
608*a90b9d01SCy Schubert #ifdef WPS_WORKAROUNDS
609*a90b9d01SCy Schubert 	start = pos;
610*a90b9d01SCy Schubert #endif /* WPS_WORKAROUNDS */
61139beb93cSSam Leffler 	end = pos + wpabuf_len(msg);
61239beb93cSSam Leffler 
61339beb93cSSam Leffler 	while (pos < end) {
61439beb93cSSam Leffler 		if (end - pos < 4) {
61539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
61639beb93cSSam Leffler 				   "%lu bytes remaining",
61739beb93cSSam Leffler 				   (unsigned long) (end - pos));
61839beb93cSSam Leffler 			return -1;
61939beb93cSSam Leffler 		}
62039beb93cSSam Leffler 
62139beb93cSSam Leffler 		type = WPA_GET_BE16(pos);
62239beb93cSSam Leffler 		pos += 2;
62339beb93cSSam Leffler 		len = WPA_GET_BE16(pos);
62439beb93cSSam Leffler 		pos += 2;
625f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
62639beb93cSSam Leffler 			   type, len);
62739beb93cSSam Leffler 		if (len > end - pos) {
62839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
629f05cddf9SRui Paulo 			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
630f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
631f05cddf9SRui Paulo 			/*
632f05cddf9SRui Paulo 			 * Some deployed APs seem to have a bug in encoding of
633f05cddf9SRui Paulo 			 * Network Key attribute in the Credential attribute
634f05cddf9SRui Paulo 			 * where they add an extra octet after the Network Key
635f05cddf9SRui Paulo 			 * attribute at least when open network is being
636f05cddf9SRui Paulo 			 * provisioned.
637f05cddf9SRui Paulo 			 */
638f05cddf9SRui Paulo 			if ((type & 0xff00) != 0x1000 &&
639f05cddf9SRui Paulo 			    prev_type == ATTR_NETWORK_KEY) {
640f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
641f05cddf9SRui Paulo 					   "to skip unexpected octet after "
642f05cddf9SRui Paulo 					   "Network Key");
643f05cddf9SRui Paulo 				pos -= 3;
644f05cddf9SRui Paulo 				continue;
645f05cddf9SRui Paulo 			}
646f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
64739beb93cSSam Leffler 			return -1;
64839beb93cSSam Leffler 		}
64939beb93cSSam Leffler 
650e28a4053SRui Paulo #ifdef WPS_WORKAROUNDS
651e28a4053SRui Paulo 		if (type == 0 && len == 0) {
652e28a4053SRui Paulo 			/*
653e28a4053SRui Paulo 			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
654e28a4053SRui Paulo 			 * end of M1. Skip those to avoid interop issues.
655e28a4053SRui Paulo 			 */
656e28a4053SRui Paulo 			int i;
657*a90b9d01SCy Schubert 
658*a90b9d01SCy Schubert 			if (last_nonzero > (size_t) (pos - start))
659*a90b9d01SCy Schubert 				continue;
660*a90b9d01SCy Schubert 
661e28a4053SRui Paulo 			for (i = 0; i < end - pos; i++) {
662*a90b9d01SCy Schubert 				if (pos[i]) {
663*a90b9d01SCy Schubert 					last_nonzero = pos - start + i;
664e28a4053SRui Paulo 					break;
665e28a4053SRui Paulo 				}
666*a90b9d01SCy Schubert 			}
667e28a4053SRui Paulo 			if (i == end - pos) {
668e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
669e28a4053SRui Paulo 					   "unexpected message padding");
670e28a4053SRui Paulo 				break;
671e28a4053SRui Paulo 			}
672e28a4053SRui Paulo 		}
673e28a4053SRui Paulo #endif /* WPS_WORKAROUNDS */
674e28a4053SRui Paulo 
67539beb93cSSam Leffler 		if (wps_set_attr(attr, type, pos, len) < 0)
67639beb93cSSam Leffler 			return -1;
67739beb93cSSam Leffler 
678f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
679f05cddf9SRui Paulo 		prev_type = type;
680f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
68139beb93cSSam Leffler 		pos += len;
68239beb93cSSam Leffler 	}
68339beb93cSSam Leffler 
68439beb93cSSam Leffler 	return 0;
68539beb93cSSam Leffler }
686