xref: /freebsd/contrib/wpa/src/wps/wps_attr_parse.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * Wi-Fi Protected Setup - attribute parsing
339beb93cSSam Leffler  * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
12*f05cddf9SRui Paulo #include "wps_defs.h"
13*f05cddf9SRui Paulo #include "wps_attr_parse.h"
1439beb93cSSam Leffler 
15*f05cddf9SRui Paulo #ifndef CONFIG_WPS_STRICT
16e28a4053SRui Paulo #define WPS_WORKAROUNDS
17*f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
18*f05cddf9SRui Paulo 
19*f05cddf9SRui Paulo 
20*f05cddf9SRui Paulo static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
21*f05cddf9SRui Paulo 					  u8 id, u8 len, const u8 *pos)
22*f05cddf9SRui Paulo {
23*f05cddf9SRui Paulo 	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
24*f05cddf9SRui Paulo 		   id, len);
25*f05cddf9SRui Paulo 	switch (id) {
26*f05cddf9SRui Paulo 	case WFA_ELEM_VERSION2:
27*f05cddf9SRui Paulo 		if (len != 1) {
28*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
29*f05cddf9SRui Paulo 				   "%u", len);
30*f05cddf9SRui Paulo 			return -1;
31*f05cddf9SRui Paulo 		}
32*f05cddf9SRui Paulo 		attr->version2 = pos;
33*f05cddf9SRui Paulo 		break;
34*f05cddf9SRui Paulo 	case WFA_ELEM_AUTHORIZEDMACS:
35*f05cddf9SRui Paulo 		attr->authorized_macs = pos;
36*f05cddf9SRui Paulo 		attr->authorized_macs_len = len;
37*f05cddf9SRui Paulo 		break;
38*f05cddf9SRui Paulo 	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
39*f05cddf9SRui Paulo 		if (len != 1) {
40*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
41*f05cddf9SRui Paulo 				   "Shareable length %u", len);
42*f05cddf9SRui Paulo 			return -1;
43*f05cddf9SRui Paulo 		}
44*f05cddf9SRui Paulo 		attr->network_key_shareable = pos;
45*f05cddf9SRui Paulo 		break;
46*f05cddf9SRui Paulo 	case WFA_ELEM_REQUEST_TO_ENROLL:
47*f05cddf9SRui Paulo 		if (len != 1) {
48*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
49*f05cddf9SRui Paulo 				   "length %u", len);
50*f05cddf9SRui Paulo 			return -1;
51*f05cddf9SRui Paulo 		}
52*f05cddf9SRui Paulo 		attr->request_to_enroll = pos;
53*f05cddf9SRui Paulo 		break;
54*f05cddf9SRui Paulo 	case WFA_ELEM_SETTINGS_DELAY_TIME:
55*f05cddf9SRui Paulo 		if (len != 1) {
56*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
57*f05cddf9SRui Paulo 				   "Time length %u", len);
58*f05cddf9SRui Paulo 			return -1;
59*f05cddf9SRui Paulo 		}
60*f05cddf9SRui Paulo 		attr->settings_delay_time = pos;
61*f05cddf9SRui Paulo 		break;
62*f05cddf9SRui Paulo 	default:
63*f05cddf9SRui Paulo 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
64*f05cddf9SRui Paulo 			   "Extension subelement %u", id);
65*f05cddf9SRui Paulo 		break;
66*f05cddf9SRui Paulo 	}
67*f05cddf9SRui Paulo 
68*f05cddf9SRui Paulo 	return 0;
69*f05cddf9SRui Paulo }
70*f05cddf9SRui Paulo 
71*f05cddf9SRui Paulo 
72*f05cddf9SRui Paulo static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
73*f05cddf9SRui Paulo 				    u16 len)
74*f05cddf9SRui Paulo {
75*f05cddf9SRui Paulo 	const u8 *end = pos + len;
76*f05cddf9SRui Paulo 	u8 id, elen;
77*f05cddf9SRui Paulo 
78*f05cddf9SRui Paulo 	while (pos + 2 < end) {
79*f05cddf9SRui Paulo 		id = *pos++;
80*f05cddf9SRui Paulo 		elen = *pos++;
81*f05cddf9SRui Paulo 		if (pos + elen > end)
82*f05cddf9SRui Paulo 			break;
83*f05cddf9SRui Paulo 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
84*f05cddf9SRui Paulo 			return -1;
85*f05cddf9SRui Paulo 		pos += elen;
86*f05cddf9SRui Paulo 	}
87*f05cddf9SRui Paulo 
88*f05cddf9SRui Paulo 	return 0;
89*f05cddf9SRui Paulo }
90*f05cddf9SRui Paulo 
91*f05cddf9SRui Paulo 
92*f05cddf9SRui Paulo static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
93*f05cddf9SRui Paulo 				u16 len)
94*f05cddf9SRui Paulo {
95*f05cddf9SRui Paulo 	u32 vendor_id;
96*f05cddf9SRui Paulo 
97*f05cddf9SRui Paulo 	if (len < 3) {
98*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
99*f05cddf9SRui Paulo 		return 0;
100*f05cddf9SRui Paulo 	}
101*f05cddf9SRui Paulo 
102*f05cddf9SRui Paulo 	vendor_id = WPA_GET_BE24(pos);
103*f05cddf9SRui Paulo 	switch (vendor_id) {
104*f05cddf9SRui Paulo 	case WPS_VENDOR_ID_WFA:
105*f05cddf9SRui Paulo 		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
106*f05cddf9SRui Paulo 	}
107*f05cddf9SRui Paulo 
108*f05cddf9SRui Paulo 	/* Handle unknown vendor extensions */
109*f05cddf9SRui Paulo 
110*f05cddf9SRui Paulo 	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
111*f05cddf9SRui Paulo 		   vendor_id);
112*f05cddf9SRui Paulo 
113*f05cddf9SRui Paulo 	if (len > WPS_MAX_VENDOR_EXT_LEN) {
114*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
115*f05cddf9SRui Paulo 			   len);
116*f05cddf9SRui Paulo 		return -1;
117*f05cddf9SRui Paulo 	}
118*f05cddf9SRui Paulo 
119*f05cddf9SRui Paulo 	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
120*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
121*f05cddf9SRui Paulo 			   "attribute (max %d vendor extensions)",
122*f05cddf9SRui Paulo 			   MAX_WPS_PARSE_VENDOR_EXT);
123*f05cddf9SRui Paulo 		return -1;
124*f05cddf9SRui Paulo 	}
125*f05cddf9SRui Paulo 	attr->vendor_ext[attr->num_vendor_ext] = pos;
126*f05cddf9SRui Paulo 	attr->vendor_ext_len[attr->num_vendor_ext] = len;
127*f05cddf9SRui Paulo 	attr->num_vendor_ext++;
128*f05cddf9SRui Paulo 
129*f05cddf9SRui Paulo 	return 0;
130*f05cddf9SRui Paulo }
131e28a4053SRui Paulo 
13239beb93cSSam Leffler 
13339beb93cSSam Leffler static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
13439beb93cSSam Leffler 			const u8 *pos, u16 len)
13539beb93cSSam Leffler {
13639beb93cSSam Leffler 	switch (type) {
13739beb93cSSam Leffler 	case ATTR_VERSION:
13839beb93cSSam Leffler 		if (len != 1) {
13939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
14039beb93cSSam Leffler 				   len);
14139beb93cSSam Leffler 			return -1;
14239beb93cSSam Leffler 		}
14339beb93cSSam Leffler 		attr->version = pos;
14439beb93cSSam Leffler 		break;
14539beb93cSSam Leffler 	case ATTR_MSG_TYPE:
14639beb93cSSam Leffler 		if (len != 1) {
14739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
14839beb93cSSam Leffler 				   "length %u", len);
14939beb93cSSam Leffler 			return -1;
15039beb93cSSam Leffler 		}
15139beb93cSSam Leffler 		attr->msg_type = pos;
15239beb93cSSam Leffler 		break;
15339beb93cSSam Leffler 	case ATTR_ENROLLEE_NONCE:
15439beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
15539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
15639beb93cSSam Leffler 				   "length %u", len);
15739beb93cSSam Leffler 			return -1;
15839beb93cSSam Leffler 		}
15939beb93cSSam Leffler 		attr->enrollee_nonce = pos;
16039beb93cSSam Leffler 		break;
16139beb93cSSam Leffler 	case ATTR_REGISTRAR_NONCE:
16239beb93cSSam Leffler 		if (len != WPS_NONCE_LEN) {
16339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
16439beb93cSSam Leffler 				   "length %u", len);
16539beb93cSSam Leffler 			return -1;
16639beb93cSSam Leffler 		}
16739beb93cSSam Leffler 		attr->registrar_nonce = pos;
16839beb93cSSam Leffler 		break;
16939beb93cSSam Leffler 	case ATTR_UUID_E:
17039beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
17139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
17239beb93cSSam Leffler 				   len);
17339beb93cSSam Leffler 			return -1;
17439beb93cSSam Leffler 		}
17539beb93cSSam Leffler 		attr->uuid_e = pos;
17639beb93cSSam Leffler 		break;
17739beb93cSSam Leffler 	case ATTR_UUID_R:
17839beb93cSSam Leffler 		if (len != WPS_UUID_LEN) {
17939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
18039beb93cSSam Leffler 				   len);
18139beb93cSSam Leffler 			return -1;
18239beb93cSSam Leffler 		}
18339beb93cSSam Leffler 		attr->uuid_r = pos;
18439beb93cSSam Leffler 		break;
18539beb93cSSam Leffler 	case ATTR_AUTH_TYPE_FLAGS:
18639beb93cSSam Leffler 		if (len != 2) {
18739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
18839beb93cSSam Leffler 				   "Type Flags length %u", len);
18939beb93cSSam Leffler 			return -1;
19039beb93cSSam Leffler 		}
19139beb93cSSam Leffler 		attr->auth_type_flags = pos;
19239beb93cSSam Leffler 		break;
19339beb93cSSam Leffler 	case ATTR_ENCR_TYPE_FLAGS:
19439beb93cSSam Leffler 		if (len != 2) {
19539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
19639beb93cSSam Leffler 				   "Flags length %u", len);
19739beb93cSSam Leffler 			return -1;
19839beb93cSSam Leffler 		}
19939beb93cSSam Leffler 		attr->encr_type_flags = pos;
20039beb93cSSam Leffler 		break;
20139beb93cSSam Leffler 	case ATTR_CONN_TYPE_FLAGS:
20239beb93cSSam Leffler 		if (len != 1) {
20339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
20439beb93cSSam Leffler 				   "Flags length %u", len);
20539beb93cSSam Leffler 			return -1;
20639beb93cSSam Leffler 		}
20739beb93cSSam Leffler 		attr->conn_type_flags = pos;
20839beb93cSSam Leffler 		break;
20939beb93cSSam Leffler 	case ATTR_CONFIG_METHODS:
21039beb93cSSam Leffler 		if (len != 2) {
21139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
21239beb93cSSam Leffler 				   "length %u", len);
21339beb93cSSam Leffler 			return -1;
21439beb93cSSam Leffler 		}
21539beb93cSSam Leffler 		attr->config_methods = pos;
21639beb93cSSam Leffler 		break;
21739beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
21839beb93cSSam Leffler 		if (len != 2) {
21939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
22039beb93cSSam Leffler 				   "Registrar Config Methods length %u", len);
22139beb93cSSam Leffler 			return -1;
22239beb93cSSam Leffler 		}
22339beb93cSSam Leffler 		attr->sel_reg_config_methods = pos;
22439beb93cSSam Leffler 		break;
22539beb93cSSam Leffler 	case ATTR_PRIMARY_DEV_TYPE:
226e28a4053SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
22739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
22839beb93cSSam Leffler 				   "Type length %u", len);
22939beb93cSSam Leffler 			return -1;
23039beb93cSSam Leffler 		}
23139beb93cSSam Leffler 		attr->primary_dev_type = pos;
23239beb93cSSam Leffler 		break;
23339beb93cSSam Leffler 	case ATTR_RF_BANDS:
23439beb93cSSam Leffler 		if (len != 1) {
23539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
23639beb93cSSam Leffler 				   "%u", len);
23739beb93cSSam Leffler 			return -1;
23839beb93cSSam Leffler 		}
23939beb93cSSam Leffler 		attr->rf_bands = pos;
24039beb93cSSam Leffler 		break;
24139beb93cSSam Leffler 	case ATTR_ASSOC_STATE:
24239beb93cSSam Leffler 		if (len != 2) {
24339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
24439beb93cSSam Leffler 				   "length %u", len);
24539beb93cSSam Leffler 			return -1;
24639beb93cSSam Leffler 		}
24739beb93cSSam Leffler 		attr->assoc_state = pos;
24839beb93cSSam Leffler 		break;
24939beb93cSSam Leffler 	case ATTR_CONFIG_ERROR:
25039beb93cSSam Leffler 		if (len != 2) {
25139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
25239beb93cSSam Leffler 				   "Error length %u", len);
25339beb93cSSam Leffler 			return -1;
25439beb93cSSam Leffler 		}
25539beb93cSSam Leffler 		attr->config_error = pos;
25639beb93cSSam Leffler 		break;
25739beb93cSSam Leffler 	case ATTR_DEV_PASSWORD_ID:
25839beb93cSSam Leffler 		if (len != 2) {
25939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
26039beb93cSSam Leffler 				   "ID length %u", len);
26139beb93cSSam Leffler 			return -1;
26239beb93cSSam Leffler 		}
26339beb93cSSam Leffler 		attr->dev_password_id = pos;
26439beb93cSSam Leffler 		break;
265e28a4053SRui Paulo 	case ATTR_OOB_DEVICE_PASSWORD:
266*f05cddf9SRui Paulo 		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
267*f05cddf9SRui Paulo 		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
268*f05cddf9SRui Paulo 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
269*f05cddf9SRui Paulo 		    WPS_OOB_DEVICE_PASSWORD_LEN) {
270e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
271e28a4053SRui Paulo 				   "Password length %u", len);
272e28a4053SRui Paulo 			return -1;
273e28a4053SRui Paulo 		}
274e28a4053SRui Paulo 		attr->oob_dev_password = pos;
275*f05cddf9SRui Paulo 		attr->oob_dev_password_len = len;
276e28a4053SRui Paulo 		break;
27739beb93cSSam Leffler 	case ATTR_OS_VERSION:
27839beb93cSSam Leffler 		if (len != 4) {
27939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
28039beb93cSSam Leffler 				   "%u", len);
28139beb93cSSam Leffler 			return -1;
28239beb93cSSam Leffler 		}
28339beb93cSSam Leffler 		attr->os_version = pos;
28439beb93cSSam Leffler 		break;
28539beb93cSSam Leffler 	case ATTR_WPS_STATE:
28639beb93cSSam Leffler 		if (len != 1) {
28739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
28839beb93cSSam Leffler 				   "Setup State length %u", len);
28939beb93cSSam Leffler 			return -1;
29039beb93cSSam Leffler 		}
29139beb93cSSam Leffler 		attr->wps_state = pos;
29239beb93cSSam Leffler 		break;
29339beb93cSSam Leffler 	case ATTR_AUTHENTICATOR:
29439beb93cSSam Leffler 		if (len != WPS_AUTHENTICATOR_LEN) {
29539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
29639beb93cSSam Leffler 				   "length %u", len);
29739beb93cSSam Leffler 			return -1;
29839beb93cSSam Leffler 		}
29939beb93cSSam Leffler 		attr->authenticator = pos;
30039beb93cSSam Leffler 		break;
30139beb93cSSam Leffler 	case ATTR_R_HASH1:
30239beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
30339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
30439beb93cSSam Leffler 				   len);
30539beb93cSSam Leffler 			return -1;
30639beb93cSSam Leffler 		}
30739beb93cSSam Leffler 		attr->r_hash1 = pos;
30839beb93cSSam Leffler 		break;
30939beb93cSSam Leffler 	case ATTR_R_HASH2:
31039beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
31139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
31239beb93cSSam Leffler 				   len);
31339beb93cSSam Leffler 			return -1;
31439beb93cSSam Leffler 		}
31539beb93cSSam Leffler 		attr->r_hash2 = pos;
31639beb93cSSam Leffler 		break;
31739beb93cSSam Leffler 	case ATTR_E_HASH1:
31839beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
31939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
32039beb93cSSam Leffler 				   len);
32139beb93cSSam Leffler 			return -1;
32239beb93cSSam Leffler 		}
32339beb93cSSam Leffler 		attr->e_hash1 = pos;
32439beb93cSSam Leffler 		break;
32539beb93cSSam Leffler 	case ATTR_E_HASH2:
32639beb93cSSam Leffler 		if (len != WPS_HASH_LEN) {
32739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
32839beb93cSSam Leffler 				   len);
32939beb93cSSam Leffler 			return -1;
33039beb93cSSam Leffler 		}
33139beb93cSSam Leffler 		attr->e_hash2 = pos;
33239beb93cSSam Leffler 		break;
33339beb93cSSam Leffler 	case ATTR_R_SNONCE1:
33439beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
33539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
33639beb93cSSam Leffler 				   "%u", len);
33739beb93cSSam Leffler 			return -1;
33839beb93cSSam Leffler 		}
33939beb93cSSam Leffler 		attr->r_snonce1 = pos;
34039beb93cSSam Leffler 		break;
34139beb93cSSam Leffler 	case ATTR_R_SNONCE2:
34239beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
34339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
34439beb93cSSam Leffler 				   "%u", len);
34539beb93cSSam Leffler 			return -1;
34639beb93cSSam Leffler 		}
34739beb93cSSam Leffler 		attr->r_snonce2 = pos;
34839beb93cSSam Leffler 		break;
34939beb93cSSam Leffler 	case ATTR_E_SNONCE1:
35039beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
35139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
35239beb93cSSam Leffler 				   "%u", len);
35339beb93cSSam Leffler 			return -1;
35439beb93cSSam Leffler 		}
35539beb93cSSam Leffler 		attr->e_snonce1 = pos;
35639beb93cSSam Leffler 		break;
35739beb93cSSam Leffler 	case ATTR_E_SNONCE2:
35839beb93cSSam Leffler 		if (len != WPS_SECRET_NONCE_LEN) {
35939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
36039beb93cSSam Leffler 				   "%u", len);
36139beb93cSSam Leffler 			return -1;
36239beb93cSSam Leffler 		}
36339beb93cSSam Leffler 		attr->e_snonce2 = pos;
36439beb93cSSam Leffler 		break;
36539beb93cSSam Leffler 	case ATTR_KEY_WRAP_AUTH:
36639beb93cSSam Leffler 		if (len != WPS_KWA_LEN) {
36739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
36839beb93cSSam Leffler 				   "Authenticator length %u", len);
36939beb93cSSam Leffler 			return -1;
37039beb93cSSam Leffler 		}
37139beb93cSSam Leffler 		attr->key_wrap_auth = pos;
37239beb93cSSam Leffler 		break;
37339beb93cSSam Leffler 	case ATTR_AUTH_TYPE:
37439beb93cSSam Leffler 		if (len != 2) {
37539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
37639beb93cSSam Leffler 				   "Type length %u", len);
37739beb93cSSam Leffler 			return -1;
37839beb93cSSam Leffler 		}
37939beb93cSSam Leffler 		attr->auth_type = pos;
38039beb93cSSam Leffler 		break;
38139beb93cSSam Leffler 	case ATTR_ENCR_TYPE:
38239beb93cSSam Leffler 		if (len != 2) {
38339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
38439beb93cSSam Leffler 				   "Type length %u", len);
38539beb93cSSam Leffler 			return -1;
38639beb93cSSam Leffler 		}
38739beb93cSSam Leffler 		attr->encr_type = pos;
38839beb93cSSam Leffler 		break;
38939beb93cSSam Leffler 	case ATTR_NETWORK_INDEX:
39039beb93cSSam Leffler 		if (len != 1) {
39139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
39239beb93cSSam Leffler 				   "length %u", len);
39339beb93cSSam Leffler 			return -1;
39439beb93cSSam Leffler 		}
39539beb93cSSam Leffler 		attr->network_idx = pos;
39639beb93cSSam Leffler 		break;
39739beb93cSSam Leffler 	case ATTR_NETWORK_KEY_INDEX:
39839beb93cSSam Leffler 		if (len != 1) {
39939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
40039beb93cSSam Leffler 				   "length %u", len);
40139beb93cSSam Leffler 			return -1;
40239beb93cSSam Leffler 		}
40339beb93cSSam Leffler 		attr->network_key_idx = pos;
40439beb93cSSam Leffler 		break;
40539beb93cSSam Leffler 	case ATTR_MAC_ADDR:
40639beb93cSSam Leffler 		if (len != ETH_ALEN) {
40739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
40839beb93cSSam Leffler 				   "length %u", len);
40939beb93cSSam Leffler 			return -1;
41039beb93cSSam Leffler 		}
41139beb93cSSam Leffler 		attr->mac_addr = pos;
41239beb93cSSam Leffler 		break;
41339beb93cSSam Leffler 	case ATTR_KEY_PROVIDED_AUTO:
41439beb93cSSam Leffler 		if (len != 1) {
41539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
41639beb93cSSam Leffler 				   "Automatically length %u", len);
41739beb93cSSam Leffler 			return -1;
41839beb93cSSam Leffler 		}
41939beb93cSSam Leffler 		attr->key_prov_auto = pos;
42039beb93cSSam Leffler 		break;
42139beb93cSSam Leffler 	case ATTR_802_1X_ENABLED:
42239beb93cSSam Leffler 		if (len != 1) {
42339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
42439beb93cSSam Leffler 				   "length %u", len);
42539beb93cSSam Leffler 			return -1;
42639beb93cSSam Leffler 		}
42739beb93cSSam Leffler 		attr->dot1x_enabled = pos;
42839beb93cSSam Leffler 		break;
42939beb93cSSam Leffler 	case ATTR_SELECTED_REGISTRAR:
43039beb93cSSam Leffler 		if (len != 1) {
43139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
43239beb93cSSam Leffler 				   " length %u", len);
43339beb93cSSam Leffler 			return -1;
43439beb93cSSam Leffler 		}
43539beb93cSSam Leffler 		attr->selected_registrar = pos;
43639beb93cSSam Leffler 		break;
43739beb93cSSam Leffler 	case ATTR_REQUEST_TYPE:
43839beb93cSSam Leffler 		if (len != 1) {
43939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
44039beb93cSSam Leffler 				   "length %u", len);
44139beb93cSSam Leffler 			return -1;
44239beb93cSSam Leffler 		}
44339beb93cSSam Leffler 		attr->request_type = pos;
44439beb93cSSam Leffler 		break;
44539beb93cSSam Leffler 	case ATTR_RESPONSE_TYPE:
44639beb93cSSam Leffler 		if (len != 1) {
44739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
44839beb93cSSam Leffler 				   "length %u", len);
44939beb93cSSam Leffler 			return -1;
45039beb93cSSam Leffler 		}
451e28a4053SRui Paulo 		attr->response_type = pos;
45239beb93cSSam Leffler 		break;
45339beb93cSSam Leffler 	case ATTR_MANUFACTURER:
45439beb93cSSam Leffler 		attr->manufacturer = pos;
45539beb93cSSam Leffler 		attr->manufacturer_len = len;
45639beb93cSSam Leffler 		break;
45739beb93cSSam Leffler 	case ATTR_MODEL_NAME:
45839beb93cSSam Leffler 		attr->model_name = pos;
45939beb93cSSam Leffler 		attr->model_name_len = len;
46039beb93cSSam Leffler 		break;
46139beb93cSSam Leffler 	case ATTR_MODEL_NUMBER:
46239beb93cSSam Leffler 		attr->model_number = pos;
46339beb93cSSam Leffler 		attr->model_number_len = len;
46439beb93cSSam Leffler 		break;
46539beb93cSSam Leffler 	case ATTR_SERIAL_NUMBER:
46639beb93cSSam Leffler 		attr->serial_number = pos;
46739beb93cSSam Leffler 		attr->serial_number_len = len;
46839beb93cSSam Leffler 		break;
46939beb93cSSam Leffler 	case ATTR_DEV_NAME:
47039beb93cSSam Leffler 		attr->dev_name = pos;
47139beb93cSSam Leffler 		attr->dev_name_len = len;
47239beb93cSSam Leffler 		break;
47339beb93cSSam Leffler 	case ATTR_PUBLIC_KEY:
47439beb93cSSam Leffler 		attr->public_key = pos;
47539beb93cSSam Leffler 		attr->public_key_len = len;
47639beb93cSSam Leffler 		break;
47739beb93cSSam Leffler 	case ATTR_ENCR_SETTINGS:
47839beb93cSSam Leffler 		attr->encr_settings = pos;
47939beb93cSSam Leffler 		attr->encr_settings_len = len;
48039beb93cSSam Leffler 		break;
48139beb93cSSam Leffler 	case ATTR_CRED:
48239beb93cSSam Leffler 		if (attr->num_cred >= MAX_CRED_COUNT) {
48339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
48439beb93cSSam Leffler 				   "attribute (max %d credentials)",
48539beb93cSSam Leffler 				   MAX_CRED_COUNT);
48639beb93cSSam Leffler 			break;
48739beb93cSSam Leffler 		}
48839beb93cSSam Leffler 		attr->cred[attr->num_cred] = pos;
48939beb93cSSam Leffler 		attr->cred_len[attr->num_cred] = len;
49039beb93cSSam Leffler 		attr->num_cred++;
49139beb93cSSam Leffler 		break;
49239beb93cSSam Leffler 	case ATTR_SSID:
49339beb93cSSam Leffler 		attr->ssid = pos;
49439beb93cSSam Leffler 		attr->ssid_len = len;
49539beb93cSSam Leffler 		break;
49639beb93cSSam Leffler 	case ATTR_NETWORK_KEY:
49739beb93cSSam Leffler 		attr->network_key = pos;
49839beb93cSSam Leffler 		attr->network_key_len = len;
49939beb93cSSam Leffler 		break;
50039beb93cSSam Leffler 	case ATTR_EAP_TYPE:
50139beb93cSSam Leffler 		attr->eap_type = pos;
50239beb93cSSam Leffler 		attr->eap_type_len = len;
50339beb93cSSam Leffler 		break;
50439beb93cSSam Leffler 	case ATTR_EAP_IDENTITY:
50539beb93cSSam Leffler 		attr->eap_identity = pos;
50639beb93cSSam Leffler 		attr->eap_identity_len = len;
50739beb93cSSam Leffler 		break;
5083157ba21SRui Paulo 	case ATTR_AP_SETUP_LOCKED:
5093157ba21SRui Paulo 		if (len != 1) {
5103157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
5113157ba21SRui Paulo 				   "length %u", len);
5123157ba21SRui Paulo 			return -1;
5133157ba21SRui Paulo 		}
5143157ba21SRui Paulo 		attr->ap_setup_locked = pos;
5153157ba21SRui Paulo 		break;
516*f05cddf9SRui Paulo 	case ATTR_REQUESTED_DEV_TYPE:
517*f05cddf9SRui Paulo 		if (len != WPS_DEV_TYPE_LEN) {
518*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
519*f05cddf9SRui Paulo 				   "Type length %u", len);
520*f05cddf9SRui Paulo 			return -1;
521*f05cddf9SRui Paulo 		}
522*f05cddf9SRui Paulo 		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
523*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
524*f05cddf9SRui Paulo 				   "Type attribute (max %u types)",
525*f05cddf9SRui Paulo 				   MAX_REQ_DEV_TYPE_COUNT);
526*f05cddf9SRui Paulo 			break;
527*f05cddf9SRui Paulo 		}
528*f05cddf9SRui Paulo 		attr->req_dev_type[attr->num_req_dev_type] = pos;
529*f05cddf9SRui Paulo 		attr->num_req_dev_type++;
530*f05cddf9SRui Paulo 		break;
531*f05cddf9SRui Paulo 	case ATTR_SECONDARY_DEV_TYPE_LIST:
532*f05cddf9SRui Paulo 		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
533*f05cddf9SRui Paulo 		    (len % WPS_DEV_TYPE_LEN) > 0) {
534*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
535*f05cddf9SRui Paulo 				   "Type length %u", len);
536*f05cddf9SRui Paulo 			return -1;
537*f05cddf9SRui Paulo 		}
538*f05cddf9SRui Paulo 		attr->sec_dev_type_list = pos;
539*f05cddf9SRui Paulo 		attr->sec_dev_type_list_len = len;
540*f05cddf9SRui Paulo 		break;
541*f05cddf9SRui Paulo 	case ATTR_VENDOR_EXT:
542*f05cddf9SRui Paulo 		if (wps_parse_vendor_ext(attr, pos, len) < 0)
543*f05cddf9SRui Paulo 			return -1;
544*f05cddf9SRui Paulo 		break;
545*f05cddf9SRui Paulo 	case ATTR_AP_CHANNEL:
546*f05cddf9SRui Paulo 		if (len != 2) {
547*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
548*f05cddf9SRui Paulo 				   "length %u", len);
549*f05cddf9SRui Paulo 			return -1;
550*f05cddf9SRui Paulo 		}
551*f05cddf9SRui Paulo 		attr->ap_channel = pos;
552*f05cddf9SRui Paulo 		break;
55339beb93cSSam Leffler 	default:
55439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
55539beb93cSSam Leffler 			   "len=%u", type, len);
55639beb93cSSam Leffler 		break;
55739beb93cSSam Leffler 	}
55839beb93cSSam Leffler 
55939beb93cSSam Leffler 	return 0;
56039beb93cSSam Leffler }
56139beb93cSSam Leffler 
56239beb93cSSam Leffler 
56339beb93cSSam Leffler int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
56439beb93cSSam Leffler {
56539beb93cSSam Leffler 	const u8 *pos, *end;
56639beb93cSSam Leffler 	u16 type, len;
567*f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
568*f05cddf9SRui Paulo 	u16 prev_type = 0;
569*f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
57039beb93cSSam Leffler 
57139beb93cSSam Leffler 	os_memset(attr, 0, sizeof(*attr));
57239beb93cSSam Leffler 	pos = wpabuf_head(msg);
57339beb93cSSam Leffler 	end = pos + wpabuf_len(msg);
57439beb93cSSam Leffler 
57539beb93cSSam Leffler 	while (pos < end) {
57639beb93cSSam Leffler 		if (end - pos < 4) {
57739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
57839beb93cSSam Leffler 				   "%lu bytes remaining",
57939beb93cSSam Leffler 				   (unsigned long) (end - pos));
58039beb93cSSam Leffler 			return -1;
58139beb93cSSam Leffler 		}
58239beb93cSSam Leffler 
58339beb93cSSam Leffler 		type = WPA_GET_BE16(pos);
58439beb93cSSam Leffler 		pos += 2;
58539beb93cSSam Leffler 		len = WPA_GET_BE16(pos);
58639beb93cSSam Leffler 		pos += 2;
587*f05cddf9SRui Paulo 		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
58839beb93cSSam Leffler 			   type, len);
58939beb93cSSam Leffler 		if (len > end - pos) {
59039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
591*f05cddf9SRui Paulo 			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
592*f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
593*f05cddf9SRui Paulo 			/*
594*f05cddf9SRui Paulo 			 * Some deployed APs seem to have a bug in encoding of
595*f05cddf9SRui Paulo 			 * Network Key attribute in the Credential attribute
596*f05cddf9SRui Paulo 			 * where they add an extra octet after the Network Key
597*f05cddf9SRui Paulo 			 * attribute at least when open network is being
598*f05cddf9SRui Paulo 			 * provisioned.
599*f05cddf9SRui Paulo 			 */
600*f05cddf9SRui Paulo 			if ((type & 0xff00) != 0x1000 &&
601*f05cddf9SRui Paulo 			    prev_type == ATTR_NETWORK_KEY) {
602*f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
603*f05cddf9SRui Paulo 					   "to skip unexpected octet after "
604*f05cddf9SRui Paulo 					   "Network Key");
605*f05cddf9SRui Paulo 				pos -= 3;
606*f05cddf9SRui Paulo 				continue;
607*f05cddf9SRui Paulo 			}
608*f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
60939beb93cSSam Leffler 			return -1;
61039beb93cSSam Leffler 		}
61139beb93cSSam Leffler 
612e28a4053SRui Paulo #ifdef WPS_WORKAROUNDS
613e28a4053SRui Paulo 		if (type == 0 && len == 0) {
614e28a4053SRui Paulo 			/*
615e28a4053SRui Paulo 			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
616e28a4053SRui Paulo 			 * end of M1. Skip those to avoid interop issues.
617e28a4053SRui Paulo 			 */
618e28a4053SRui Paulo 			int i;
619e28a4053SRui Paulo 			for (i = 0; i < end - pos; i++) {
620e28a4053SRui Paulo 				if (pos[i])
621e28a4053SRui Paulo 					break;
622e28a4053SRui Paulo 			}
623e28a4053SRui Paulo 			if (i == end - pos) {
624e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
625e28a4053SRui Paulo 					   "unexpected message padding");
626e28a4053SRui Paulo 				break;
627e28a4053SRui Paulo 			}
628e28a4053SRui Paulo 		}
629e28a4053SRui Paulo #endif /* WPS_WORKAROUNDS */
630e28a4053SRui Paulo 
63139beb93cSSam Leffler 		if (wps_set_attr(attr, type, pos, len) < 0)
63239beb93cSSam Leffler 			return -1;
63339beb93cSSam Leffler 
634*f05cddf9SRui Paulo #ifdef WPS_WORKAROUNDS
635*f05cddf9SRui Paulo 		prev_type = type;
636*f05cddf9SRui Paulo #endif /* WPS_WORKAROUNDS */
63739beb93cSSam Leffler 		pos += len;
63839beb93cSSam Leffler 	}
63939beb93cSSam Leffler 
64039beb93cSSam Leffler 	return 0;
64139beb93cSSam Leffler }
642