xref: /freebsd/contrib/wpa/src/wps/wps_attr_parse.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
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 
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;
62*5b9c547cSRui Paulo 	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
63*5b9c547cSRui Paulo 		if (len != 2) {
64*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
65*5b9c547cSRui Paulo 				   len);
66*5b9c547cSRui Paulo 			return -1;
67*5b9c547cSRui Paulo 		}
68*5b9c547cSRui Paulo 		attr->registrar_configuration_methods = pos;
69*5b9c547cSRui Paulo 		break;
70f05cddf9SRui Paulo 	default:
71f05cddf9SRui Paulo 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
72f05cddf9SRui Paulo 			   "Extension subelement %u", id);
73f05cddf9SRui Paulo 		break;
74f05cddf9SRui Paulo 	}
75f05cddf9SRui Paulo 
76f05cddf9SRui Paulo 	return 0;
77f05cddf9SRui Paulo }
78f05cddf9SRui Paulo 
79f05cddf9SRui Paulo 
80f05cddf9SRui Paulo static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
81f05cddf9SRui Paulo 				    u16 len)
82f05cddf9SRui Paulo {
83f05cddf9SRui Paulo 	const u8 *end = pos + len;
84f05cddf9SRui Paulo 	u8 id, elen;
85f05cddf9SRui Paulo 
86*5b9c547cSRui Paulo 	while (pos + 2 <= end) {
87f05cddf9SRui Paulo 		id = *pos++;
88f05cddf9SRui Paulo 		elen = *pos++;
89f05cddf9SRui Paulo 		if (pos + elen > end)
90f05cddf9SRui Paulo 			break;
91f05cddf9SRui Paulo 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
92f05cddf9SRui Paulo 			return -1;
93f05cddf9SRui Paulo 		pos += elen;
94f05cddf9SRui Paulo 	}
95f05cddf9SRui Paulo 
96f05cddf9SRui Paulo 	return 0;
97f05cddf9SRui Paulo }
98f05cddf9SRui Paulo 
99f05cddf9SRui Paulo 
100f05cddf9SRui Paulo static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
101f05cddf9SRui Paulo 				u16 len)
102f05cddf9SRui Paulo {
103f05cddf9SRui Paulo 	u32 vendor_id;
104f05cddf9SRui Paulo 
105f05cddf9SRui Paulo 	if (len < 3) {
106f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
107f05cddf9SRui Paulo 		return 0;
108f05cddf9SRui Paulo 	}
109f05cddf9SRui Paulo 
110f05cddf9SRui Paulo 	vendor_id = WPA_GET_BE24(pos);
111f05cddf9SRui Paulo 	switch (vendor_id) {
112f05cddf9SRui Paulo 	case WPS_VENDOR_ID_WFA:
113f05cddf9SRui Paulo 		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
114f05cddf9SRui Paulo 	}
115f05cddf9SRui Paulo 
116f05cddf9SRui Paulo 	/* Handle unknown vendor extensions */
117f05cddf9SRui Paulo 
118f05cddf9SRui Paulo 	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
119f05cddf9SRui Paulo 		   vendor_id);
120f05cddf9SRui Paulo 
121f05cddf9SRui Paulo 	if (len > WPS_MAX_VENDOR_EXT_LEN) {
122f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
123f05cddf9SRui Paulo 			   len);
124f05cddf9SRui Paulo 		return -1;
125f05cddf9SRui Paulo 	}
126f05cddf9SRui Paulo 
127f05cddf9SRui Paulo 	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
128f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
129f05cddf9SRui Paulo 			   "attribute (max %d vendor extensions)",
130f05cddf9SRui Paulo 			   MAX_WPS_PARSE_VENDOR_EXT);
131f05cddf9SRui Paulo 		return -1;
132f05cddf9SRui Paulo 	}
133f05cddf9SRui Paulo 	attr->vendor_ext[attr->num_vendor_ext] = pos;
134f05cddf9SRui Paulo 	attr->vendor_ext_len[attr->num_vendor_ext] = len;
135f05cddf9SRui Paulo 	attr->num_vendor_ext++;
136f05cddf9SRui Paulo 
137f05cddf9SRui Paulo 	return 0;
138f05cddf9SRui Paulo }
139e28a4053SRui Paulo 
14039beb93cSSam Leffler 
14139beb93cSSam Leffler static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
14239beb93cSSam Leffler 			const u8 *pos, u16 len)
14339beb93cSSam Leffler {
14439beb93cSSam Leffler 	switch (type) {
14539beb93cSSam Leffler 	case ATTR_VERSION:
14639beb93cSSam Leffler 		if (len != 1) {
14739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
14839beb93cSSam Leffler 				   len);
14939beb93cSSam Leffler 			return -1;
15039beb93cSSam Leffler 		}
15139beb93cSSam Leffler 		attr->version = pos;
15239beb93cSSam Leffler 		break;
15339beb93cSSam Leffler 	case ATTR_MSG_TYPE:
15439beb93cSSam Leffler 		if (len != 1) {
15539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
15639beb93cSSam Leffler 				   "length %u", len);
15739beb93cSSam Leffler 			return -1;
15839beb93cSSam Leffler 		}
15939beb93cSSam Leffler 		attr->msg_type = pos;
16039beb93cSSam Leffler 		break;
16139beb93cSSam Leffler 	case ATTR_ENROLLEE_NONCE:
16239beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
16339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
16439beb93cSSam Leffler 				   "length %u", len);
16539beb93cSSam Leffler 			return -1;
16639beb93cSSam Leffler 		}
16739beb93cSSam Leffler 		attr->enrollee_nonce = pos;
16839beb93cSSam Leffler 		break;
16939beb93cSSam Leffler 	case ATTR_REGISTRAR_NONCE:
17039beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
17139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
17239beb93cSSam Leffler 				   "length %u", len);
17339beb93cSSam Leffler 			return -1;
17439beb93cSSam Leffler 		}
17539beb93cSSam Leffler 		attr->registrar_nonce = pos;
17639beb93cSSam Leffler 		break;
17739beb93cSSam Leffler 	case ATTR_UUID_E:
17839beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
17939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
18039beb93cSSam Leffler 				   len);
18139beb93cSSam Leffler 			return -1;
18239beb93cSSam Leffler 		}
18339beb93cSSam Leffler 		attr->uuid_e = pos;
18439beb93cSSam Leffler 		break;
18539beb93cSSam Leffler 	case ATTR_UUID_R:
18639beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
18739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
18839beb93cSSam Leffler 				   len);
18939beb93cSSam Leffler 			return -1;
19039beb93cSSam Leffler 		}
19139beb93cSSam Leffler 		attr->uuid_r = pos;
19239beb93cSSam Leffler 		break;
19339beb93cSSam Leffler 	case ATTR_AUTH_TYPE_FLAGS:
19439beb93cSSam Leffler 		if (len != 2) {
19539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
19639beb93cSSam Leffler 				   "Type Flags length %u", len);
19739beb93cSSam Leffler 			return -1;
19839beb93cSSam Leffler 		}
19939beb93cSSam Leffler 		attr->auth_type_flags = pos;
20039beb93cSSam Leffler 		break;
20139beb93cSSam Leffler 	case ATTR_ENCR_TYPE_FLAGS:
20239beb93cSSam Leffler 		if (len != 2) {
20339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
20439beb93cSSam Leffler 				   "Flags length %u", len);
20539beb93cSSam Leffler 			return -1;
20639beb93cSSam Leffler 		}
20739beb93cSSam Leffler 		attr->encr_type_flags = pos;
20839beb93cSSam Leffler 		break;
20939beb93cSSam Leffler 	case ATTR_CONN_TYPE_FLAGS:
21039beb93cSSam Leffler 		if (len != 1) {
21139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
21239beb93cSSam Leffler 				   "Flags length %u", len);
21339beb93cSSam Leffler 			return -1;
21439beb93cSSam Leffler 		}
21539beb93cSSam Leffler 		attr->conn_type_flags = pos;
21639beb93cSSam Leffler 		break;
21739beb93cSSam Leffler 	case ATTR_CONFIG_METHODS:
21839beb93cSSam Leffler 		if (len != 2) {
21939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
22039beb93cSSam Leffler 				   "length %u", len);
22139beb93cSSam Leffler 			return -1;
22239beb93cSSam Leffler 		}
22339beb93cSSam Leffler 		attr->config_methods = pos;
22439beb93cSSam Leffler 		break;
22539beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
22639beb93cSSam Leffler 		if (len != 2) {
22739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
22839beb93cSSam Leffler 				   "Registrar Config Methods length %u", len);
22939beb93cSSam Leffler 			return -1;
23039beb93cSSam Leffler 		}
23139beb93cSSam Leffler 		attr->sel_reg_config_methods = pos;
23239beb93cSSam Leffler 		break;
23339beb93cSSam Leffler 	case ATTR_PRIMARY_DEV_TYPE:
234e28a4053SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
23539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
23639beb93cSSam Leffler 				   "Type length %u", len);
23739beb93cSSam Leffler 			return -1;
23839beb93cSSam Leffler 		}
23939beb93cSSam Leffler 		attr->primary_dev_type = pos;
24039beb93cSSam Leffler 		break;
24139beb93cSSam Leffler 	case ATTR_RF_BANDS:
24239beb93cSSam Leffler 		if (len != 1) {
24339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
24439beb93cSSam Leffler 				   "%u", len);
24539beb93cSSam Leffler 			return -1;
24639beb93cSSam Leffler 		}
24739beb93cSSam Leffler 		attr->rf_bands = pos;
24839beb93cSSam Leffler 		break;
24939beb93cSSam Leffler 	case ATTR_ASSOC_STATE:
25039beb93cSSam Leffler 		if (len != 2) {
25139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
25239beb93cSSam Leffler 				   "length %u", len);
25339beb93cSSam Leffler 			return -1;
25439beb93cSSam Leffler 		}
25539beb93cSSam Leffler 		attr->assoc_state = pos;
25639beb93cSSam Leffler 		break;
25739beb93cSSam Leffler 	case ATTR_CONFIG_ERROR:
25839beb93cSSam Leffler 		if (len != 2) {
25939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
26039beb93cSSam Leffler 				   "Error length %u", len);
26139beb93cSSam Leffler 			return -1;
26239beb93cSSam Leffler 		}
26339beb93cSSam Leffler 		attr->config_error = pos;
26439beb93cSSam Leffler 		break;
26539beb93cSSam Leffler 	case ATTR_DEV_PASSWORD_ID:
26639beb93cSSam Leffler 		if (len != 2) {
26739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
26839beb93cSSam Leffler 				   "ID length %u", len);
26939beb93cSSam Leffler 			return -1;
27039beb93cSSam Leffler 		}
27139beb93cSSam Leffler 		attr->dev_password_id = pos;
27239beb93cSSam Leffler 		break;
273e28a4053SRui Paulo 	case ATTR_OOB_DEVICE_PASSWORD:
274*5b9c547cSRui Paulo 		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
275f05cddf9SRui Paulo 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
276*5b9c547cSRui Paulo 		    WPS_OOB_DEVICE_PASSWORD_LEN ||
277*5b9c547cSRui Paulo 		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
278*5b9c547cSRui Paulo 		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
279*5b9c547cSRui Paulo 		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
280*5b9c547cSRui Paulo 		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
281e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
282e28a4053SRui Paulo 				   "Password length %u", len);
283e28a4053SRui Paulo 			return -1;
284e28a4053SRui Paulo 		}
285e28a4053SRui Paulo 		attr->oob_dev_password = pos;
286f05cddf9SRui Paulo 		attr->oob_dev_password_len = len;
287e28a4053SRui Paulo 		break;
28839beb93cSSam Leffler 	case ATTR_OS_VERSION:
28939beb93cSSam Leffler 		if (len != 4) {
29039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
29139beb93cSSam Leffler 				   "%u", len);
29239beb93cSSam Leffler 			return -1;
29339beb93cSSam Leffler 		}
29439beb93cSSam Leffler 		attr->os_version = pos;
29539beb93cSSam Leffler 		break;
29639beb93cSSam Leffler 	case ATTR_WPS_STATE:
29739beb93cSSam Leffler 		if (len != 1) {
29839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
29939beb93cSSam Leffler 				   "Setup State length %u", len);
30039beb93cSSam Leffler 			return -1;
30139beb93cSSam Leffler 		}
30239beb93cSSam Leffler 		attr->wps_state = pos;
30339beb93cSSam Leffler 		break;
30439beb93cSSam Leffler 	case ATTR_AUTHENTICATOR:
30539beb93cSSam Leffler 		if (len != WPS_AUTHENTICATOR_LEN) {
30639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
30739beb93cSSam Leffler 				   "length %u", len);
30839beb93cSSam Leffler 			return -1;
30939beb93cSSam Leffler 		}
31039beb93cSSam Leffler 		attr->authenticator = pos;
31139beb93cSSam Leffler 		break;
31239beb93cSSam Leffler 	case ATTR_R_HASH1:
31339beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
31439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
31539beb93cSSam Leffler 				   len);
31639beb93cSSam Leffler 			return -1;
31739beb93cSSam Leffler 		}
31839beb93cSSam Leffler 		attr->r_hash1 = pos;
31939beb93cSSam Leffler 		break;
32039beb93cSSam Leffler 	case ATTR_R_HASH2:
32139beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
32239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
32339beb93cSSam Leffler 				   len);
32439beb93cSSam Leffler 			return -1;
32539beb93cSSam Leffler 		}
32639beb93cSSam Leffler 		attr->r_hash2 = pos;
32739beb93cSSam Leffler 		break;
32839beb93cSSam Leffler 	case ATTR_E_HASH1:
32939beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
33039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
33139beb93cSSam Leffler 				   len);
33239beb93cSSam Leffler 			return -1;
33339beb93cSSam Leffler 		}
33439beb93cSSam Leffler 		attr->e_hash1 = pos;
33539beb93cSSam Leffler 		break;
33639beb93cSSam Leffler 	case ATTR_E_HASH2:
33739beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
33839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
33939beb93cSSam Leffler 				   len);
34039beb93cSSam Leffler 			return -1;
34139beb93cSSam Leffler 		}
34239beb93cSSam Leffler 		attr->e_hash2 = pos;
34339beb93cSSam Leffler 		break;
34439beb93cSSam Leffler 	case ATTR_R_SNONCE1:
34539beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
34639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
34739beb93cSSam Leffler 				   "%u", len);
34839beb93cSSam Leffler 			return -1;
34939beb93cSSam Leffler 		}
35039beb93cSSam Leffler 		attr->r_snonce1 = pos;
35139beb93cSSam Leffler 		break;
35239beb93cSSam Leffler 	case ATTR_R_SNONCE2:
35339beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
35439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
35539beb93cSSam Leffler 				   "%u", len);
35639beb93cSSam Leffler 			return -1;
35739beb93cSSam Leffler 		}
35839beb93cSSam Leffler 		attr->r_snonce2 = pos;
35939beb93cSSam Leffler 		break;
36039beb93cSSam Leffler 	case ATTR_E_SNONCE1:
36139beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
36239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
36339beb93cSSam Leffler 				   "%u", len);
36439beb93cSSam Leffler 			return -1;
36539beb93cSSam Leffler 		}
36639beb93cSSam Leffler 		attr->e_snonce1 = pos;
36739beb93cSSam Leffler 		break;
36839beb93cSSam Leffler 	case ATTR_E_SNONCE2:
36939beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
37039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
37139beb93cSSam Leffler 				   "%u", len);
37239beb93cSSam Leffler 			return -1;
37339beb93cSSam Leffler 		}
37439beb93cSSam Leffler 		attr->e_snonce2 = pos;
37539beb93cSSam Leffler 		break;
37639beb93cSSam Leffler 	case ATTR_KEY_WRAP_AUTH:
37739beb93cSSam Leffler 		if (len != WPS_KWA_LEN) {
37839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
37939beb93cSSam Leffler 				   "Authenticator length %u", len);
38039beb93cSSam Leffler 			return -1;
38139beb93cSSam Leffler 		}
38239beb93cSSam Leffler 		attr->key_wrap_auth = pos;
38339beb93cSSam Leffler 		break;
38439beb93cSSam Leffler 	case ATTR_AUTH_TYPE:
38539beb93cSSam Leffler 		if (len != 2) {
38639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
38739beb93cSSam Leffler 				   "Type length %u", len);
38839beb93cSSam Leffler 			return -1;
38939beb93cSSam Leffler 		}
39039beb93cSSam Leffler 		attr->auth_type = pos;
39139beb93cSSam Leffler 		break;
39239beb93cSSam Leffler 	case ATTR_ENCR_TYPE:
39339beb93cSSam Leffler 		if (len != 2) {
39439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
39539beb93cSSam Leffler 				   "Type length %u", len);
39639beb93cSSam Leffler 			return -1;
39739beb93cSSam Leffler 		}
39839beb93cSSam Leffler 		attr->encr_type = pos;
39939beb93cSSam Leffler 		break;
40039beb93cSSam Leffler 	case ATTR_NETWORK_INDEX:
40139beb93cSSam Leffler 		if (len != 1) {
40239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
40339beb93cSSam Leffler 				   "length %u", len);
40439beb93cSSam Leffler 			return -1;
40539beb93cSSam Leffler 		}
40639beb93cSSam Leffler 		attr->network_idx = pos;
40739beb93cSSam Leffler 		break;
40839beb93cSSam Leffler 	case ATTR_NETWORK_KEY_INDEX:
40939beb93cSSam Leffler 		if (len != 1) {
41039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
41139beb93cSSam Leffler 				   "length %u", len);
41239beb93cSSam Leffler 			return -1;
41339beb93cSSam Leffler 		}
41439beb93cSSam Leffler 		attr->network_key_idx = pos;
41539beb93cSSam Leffler 		break;
41639beb93cSSam Leffler 	case ATTR_MAC_ADDR:
41739beb93cSSam Leffler 		if (len != ETH_ALEN) {
41839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
41939beb93cSSam Leffler 				   "length %u", len);
42039beb93cSSam Leffler 			return -1;
42139beb93cSSam Leffler 		}
42239beb93cSSam Leffler 		attr->mac_addr = pos;
42339beb93cSSam Leffler 		break;
42439beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR:
42539beb93cSSam Leffler 		if (len != 1) {
42639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
42739beb93cSSam Leffler 				   " length %u", len);
42839beb93cSSam Leffler 			return -1;
42939beb93cSSam Leffler 		}
43039beb93cSSam Leffler 		attr->selected_registrar = pos;
43139beb93cSSam Leffler 		break;
43239beb93cSSam Leffler 	case ATTR_REQUEST_TYPE:
43339beb93cSSam Leffler 		if (len != 1) {
43439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
43539beb93cSSam Leffler 				   "length %u", len);
43639beb93cSSam Leffler 			return -1;
43739beb93cSSam Leffler 		}
43839beb93cSSam Leffler 		attr->request_type = pos;
43939beb93cSSam Leffler 		break;
44039beb93cSSam Leffler 	case ATTR_RESPONSE_TYPE:
44139beb93cSSam Leffler 		if (len != 1) {
44239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
44339beb93cSSam Leffler 				   "length %u", len);
44439beb93cSSam Leffler 			return -1;
44539beb93cSSam Leffler 		}
446e28a4053SRui Paulo 		attr->response_type = pos;
44739beb93cSSam Leffler 		break;
44839beb93cSSam Leffler 	case ATTR_MANUFACTURER:
44939beb93cSSam Leffler 		attr->manufacturer = pos;
45039beb93cSSam Leffler 		attr->manufacturer_len = len;
45139beb93cSSam Leffler 		break;
45239beb93cSSam Leffler 	case ATTR_MODEL_NAME:
45339beb93cSSam Leffler 		attr->model_name = pos;
45439beb93cSSam Leffler 		attr->model_name_len = len;
45539beb93cSSam Leffler 		break;
45639beb93cSSam Leffler 	case ATTR_MODEL_NUMBER:
45739beb93cSSam Leffler 		attr->model_number = pos;
45839beb93cSSam Leffler 		attr->model_number_len = len;
45939beb93cSSam Leffler 		break;
46039beb93cSSam Leffler 	case ATTR_SERIAL_NUMBER:
46139beb93cSSam Leffler 		attr->serial_number = pos;
46239beb93cSSam Leffler 		attr->serial_number_len = len;
46339beb93cSSam Leffler 		break;
46439beb93cSSam Leffler 	case ATTR_DEV_NAME:
46539beb93cSSam Leffler 		attr->dev_name = pos;
46639beb93cSSam Leffler 		attr->dev_name_len = len;
46739beb93cSSam Leffler 		break;
46839beb93cSSam Leffler 	case ATTR_PUBLIC_KEY:
46939beb93cSSam Leffler 		attr->public_key = pos;
47039beb93cSSam Leffler 		attr->public_key_len = len;
47139beb93cSSam Leffler 		break;
47239beb93cSSam Leffler 	case ATTR_ENCR_SETTINGS:
47339beb93cSSam Leffler 		attr->encr_settings = pos;
47439beb93cSSam Leffler 		attr->encr_settings_len = len;
47539beb93cSSam Leffler 		break;
47639beb93cSSam Leffler 	case ATTR_CRED:
47739beb93cSSam Leffler 		if (attr->num_cred >= MAX_CRED_COUNT) {
47839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
47939beb93cSSam Leffler 				   "attribute (max %d credentials)",
48039beb93cSSam Leffler 				   MAX_CRED_COUNT);
48139beb93cSSam Leffler 			break;
48239beb93cSSam Leffler 		}
48339beb93cSSam Leffler 		attr->cred[attr->num_cred] = pos;
48439beb93cSSam Leffler 		attr->cred_len[attr->num_cred] = len;
48539beb93cSSam Leffler 		attr->num_cred++;
48639beb93cSSam Leffler 		break;
48739beb93cSSam Leffler 	case ATTR_SSID:
48839beb93cSSam Leffler 		attr->ssid = pos;
48939beb93cSSam Leffler 		attr->ssid_len = len;
49039beb93cSSam Leffler 		break;
49139beb93cSSam Leffler 	case ATTR_NETWORK_KEY:
49239beb93cSSam Leffler 		attr->network_key = pos;
49339beb93cSSam Leffler 		attr->network_key_len = len;
49439beb93cSSam Leffler 		break;
4953157ba21SRui Paulo 	case ATTR_AP_SETUP_LOCKED:
4963157ba21SRui Paulo 		if (len != 1) {
4973157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
4983157ba21SRui Paulo 				   "length %u", len);
4993157ba21SRui Paulo 			return -1;
5003157ba21SRui Paulo 		}
5013157ba21SRui Paulo 		attr->ap_setup_locked = pos;
5023157ba21SRui Paulo 		break;
503f05cddf9SRui Paulo 	case ATTR_REQUESTED_DEV_TYPE:
504f05cddf9SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
505f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
506f05cddf9SRui Paulo 				   "Type length %u", len);
507f05cddf9SRui Paulo 			return -1;
508f05cddf9SRui Paulo 		}
509f05cddf9SRui Paulo 		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
510f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
511f05cddf9SRui Paulo 				   "Type attribute (max %u types)",
512f05cddf9SRui Paulo 				   MAX_REQ_DEV_TYPE_COUNT);
513f05cddf9SRui Paulo 			break;
514f05cddf9SRui Paulo 		}
515f05cddf9SRui Paulo 		attr->req_dev_type[attr->num_req_dev_type] = pos;
516f05cddf9SRui Paulo 		attr->num_req_dev_type++;
517f05cddf9SRui Paulo 		break;
518f05cddf9SRui Paulo 	case ATTR_SECONDARY_DEV_TYPE_LIST:
519f05cddf9SRui Paulo 		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
520f05cddf9SRui Paulo 		    (len % WPS_DEV_TYPE_LEN) > 0) {
521f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
522f05cddf9SRui Paulo 				   "Type length %u", len);
523f05cddf9SRui Paulo 			return -1;
524f05cddf9SRui Paulo 		}
525f05cddf9SRui Paulo 		attr->sec_dev_type_list = pos;
526f05cddf9SRui Paulo 		attr->sec_dev_type_list_len = len;
527f05cddf9SRui Paulo 		break;
528f05cddf9SRui Paulo 	case ATTR_VENDOR_EXT:
529f05cddf9SRui Paulo 		if (wps_parse_vendor_ext(attr, pos, len) < 0)
530f05cddf9SRui Paulo 			return -1;
531f05cddf9SRui Paulo 		break;
532f05cddf9SRui Paulo 	case ATTR_AP_CHANNEL:
533f05cddf9SRui Paulo 		if (len != 2) {
534f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
535f05cddf9SRui Paulo 				   "length %u", len);
536f05cddf9SRui Paulo 			return -1;
537f05cddf9SRui Paulo 		}
538f05cddf9SRui Paulo 		attr->ap_channel = pos;
539f05cddf9SRui Paulo 		break;
54039beb93cSSam Leffler 	default:
54139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
54239beb93cSSam Leffler 			   "len=%u", type, len);
54339beb93cSSam Leffler 		break;
54439beb93cSSam Leffler 	}
54539beb93cSSam Leffler 
54639beb93cSSam Leffler 	return 0;
54739beb93cSSam Leffler }
54839beb93cSSam Leffler 
54939beb93cSSam Leffler 
55039beb93cSSam Leffler int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
55139beb93cSSam Leffler {
55239beb93cSSam Leffler 	const u8 *pos, *end;
55339beb93cSSam Leffler 	u16 type, len;
554f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
555f05cddf9SRui Paulo 	u16 prev_type = 0;
556f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
55739beb93cSSam Leffler 
55839beb93cSSam Leffler 	os_memset(attr, 0, sizeof(*attr));
55939beb93cSSam Leffler 	pos = wpabuf_head(msg);
56039beb93cSSam Leffler 	end = pos + wpabuf_len(msg);
56139beb93cSSam Leffler 
56239beb93cSSam Leffler 	while (pos < end) {
56339beb93cSSam Leffler 		if (end - pos < 4) {
56439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
56539beb93cSSam Leffler 				   "%lu bytes remaining",
56639beb93cSSam Leffler 				   (unsigned long) (end - pos));
56739beb93cSSam Leffler 			return -1;
56839beb93cSSam Leffler 		}
56939beb93cSSam Leffler 
57039beb93cSSam Leffler 		type = WPA_GET_BE16(pos);
57139beb93cSSam Leffler 		pos += 2;
57239beb93cSSam Leffler 		len = WPA_GET_BE16(pos);
57339beb93cSSam Leffler 		pos += 2;
574f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
57539beb93cSSam Leffler 			   type, len);
57639beb93cSSam Leffler 		if (len > end - pos) {
57739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
578f05cddf9SRui Paulo 			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
579f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
580f05cddf9SRui Paulo 			/*
581f05cddf9SRui Paulo 			 * Some deployed APs seem to have a bug in encoding of
582f05cddf9SRui Paulo 			 * Network Key attribute in the Credential attribute
583f05cddf9SRui Paulo 			 * where they add an extra octet after the Network Key
584f05cddf9SRui Paulo 			 * attribute at least when open network is being
585f05cddf9SRui Paulo 			 * provisioned.
586f05cddf9SRui Paulo 			 */
587f05cddf9SRui Paulo 			if ((type & 0xff00) != 0x1000 &&
588f05cddf9SRui Paulo 			    prev_type == ATTR_NETWORK_KEY) {
589f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
590f05cddf9SRui Paulo 					   "to skip unexpected octet after "
591f05cddf9SRui Paulo 					   "Network Key");
592f05cddf9SRui Paulo 				pos -= 3;
593f05cddf9SRui Paulo 				continue;
594f05cddf9SRui Paulo 			}
595f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
59639beb93cSSam Leffler 			return -1;
59739beb93cSSam Leffler 		}
59839beb93cSSam Leffler 
599e28a4053SRui Paulo #ifdef WPS_WORKAROUNDS
600e28a4053SRui Paulo 		if (type == 0 && len == 0) {
601e28a4053SRui Paulo 			/*
602e28a4053SRui Paulo 			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
603e28a4053SRui Paulo 			 * end of M1. Skip those to avoid interop issues.
604e28a4053SRui Paulo 			 */
605e28a4053SRui Paulo 			int i;
606e28a4053SRui Paulo 			for (i = 0; i < end - pos; i++) {
607e28a4053SRui Paulo 				if (pos[i])
608e28a4053SRui Paulo 					break;
609e28a4053SRui Paulo 			}
610e28a4053SRui Paulo 			if (i == end - pos) {
611e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
612e28a4053SRui Paulo 					   "unexpected message padding");
613e28a4053SRui Paulo 				break;
614e28a4053SRui Paulo 			}
615e28a4053SRui Paulo 		}
616e28a4053SRui Paulo #endif /* WPS_WORKAROUNDS */
617e28a4053SRui Paulo 
61839beb93cSSam Leffler 		if (wps_set_attr(attr, type, pos, len) < 0)
61939beb93cSSam Leffler 			return -1;
62039beb93cSSam Leffler 
621f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
622f05cddf9SRui Paulo 		prev_type = type;
623f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
62439beb93cSSam Leffler 		pos += len;
62539beb93cSSam Leffler 	}
62639beb93cSSam Leffler 
62739beb93cSSam Leffler 	return 0;
62839beb93cSSam Leffler }
629