1189251Ssam/*
2189251Ssam * Wi-Fi Protected Setup - attribute parsing
3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12252726Srpaulo#include "wps_defs.h"
13252726Srpaulo#include "wps_attr_parse.h"
14189251Ssam
15252726Srpaulo#ifndef CONFIG_WPS_STRICT
16214734Srpaulo#define WPS_WORKAROUNDS
17252726Srpaulo#endif /* CONFIG_WPS_STRICT */
18189251Ssam
19214734Srpaulo
20252726Srpaulostatic int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
21252726Srpaulo					  u8 id, u8 len, const u8 *pos)
22252726Srpaulo{
23252726Srpaulo	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
24252726Srpaulo		   id, len);
25252726Srpaulo	switch (id) {
26252726Srpaulo	case WFA_ELEM_VERSION2:
27252726Srpaulo		if (len != 1) {
28252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
29252726Srpaulo				   "%u", len);
30252726Srpaulo			return -1;
31252726Srpaulo		}
32252726Srpaulo		attr->version2 = pos;
33252726Srpaulo		break;
34252726Srpaulo	case WFA_ELEM_AUTHORIZEDMACS:
35252726Srpaulo		attr->authorized_macs = pos;
36252726Srpaulo		attr->authorized_macs_len = len;
37252726Srpaulo		break;
38252726Srpaulo	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
39252726Srpaulo		if (len != 1) {
40252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
41252726Srpaulo				   "Shareable length %u", len);
42252726Srpaulo			return -1;
43252726Srpaulo		}
44252726Srpaulo		attr->network_key_shareable = pos;
45252726Srpaulo		break;
46252726Srpaulo	case WFA_ELEM_REQUEST_TO_ENROLL:
47252726Srpaulo		if (len != 1) {
48252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
49252726Srpaulo				   "length %u", len);
50252726Srpaulo			return -1;
51252726Srpaulo		}
52252726Srpaulo		attr->request_to_enroll = pos;
53252726Srpaulo		break;
54252726Srpaulo	case WFA_ELEM_SETTINGS_DELAY_TIME:
55252726Srpaulo		if (len != 1) {
56252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
57252726Srpaulo				   "Time length %u", len);
58252726Srpaulo			return -1;
59252726Srpaulo		}
60252726Srpaulo		attr->settings_delay_time = pos;
61252726Srpaulo		break;
62252726Srpaulo	default:
63252726Srpaulo		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
64252726Srpaulo			   "Extension subelement %u", id);
65252726Srpaulo		break;
66252726Srpaulo	}
67252726Srpaulo
68252726Srpaulo	return 0;
69252726Srpaulo}
70252726Srpaulo
71252726Srpaulo
72252726Srpaulostatic int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
73252726Srpaulo				    u16 len)
74252726Srpaulo{
75252726Srpaulo	const u8 *end = pos + len;
76252726Srpaulo	u8 id, elen;
77252726Srpaulo
78252726Srpaulo	while (pos + 2 < end) {
79252726Srpaulo		id = *pos++;
80252726Srpaulo		elen = *pos++;
81252726Srpaulo		if (pos + elen > end)
82252726Srpaulo			break;
83252726Srpaulo		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
84252726Srpaulo			return -1;
85252726Srpaulo		pos += elen;
86252726Srpaulo	}
87252726Srpaulo
88252726Srpaulo	return 0;
89252726Srpaulo}
90252726Srpaulo
91252726Srpaulo
92252726Srpaulostatic int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
93252726Srpaulo				u16 len)
94252726Srpaulo{
95252726Srpaulo	u32 vendor_id;
96252726Srpaulo
97252726Srpaulo	if (len < 3) {
98252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
99252726Srpaulo		return 0;
100252726Srpaulo	}
101252726Srpaulo
102252726Srpaulo	vendor_id = WPA_GET_BE24(pos);
103252726Srpaulo	switch (vendor_id) {
104252726Srpaulo	case WPS_VENDOR_ID_WFA:
105252726Srpaulo		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
106252726Srpaulo	}
107252726Srpaulo
108252726Srpaulo	/* Handle unknown vendor extensions */
109252726Srpaulo
110252726Srpaulo	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
111252726Srpaulo		   vendor_id);
112252726Srpaulo
113252726Srpaulo	if (len > WPS_MAX_VENDOR_EXT_LEN) {
114252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
115252726Srpaulo			   len);
116252726Srpaulo		return -1;
117252726Srpaulo	}
118252726Srpaulo
119252726Srpaulo	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
120252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
121252726Srpaulo			   "attribute (max %d vendor extensions)",
122252726Srpaulo			   MAX_WPS_PARSE_VENDOR_EXT);
123252726Srpaulo		return -1;
124252726Srpaulo	}
125252726Srpaulo	attr->vendor_ext[attr->num_vendor_ext] = pos;
126252726Srpaulo	attr->vendor_ext_len[attr->num_vendor_ext] = len;
127252726Srpaulo	attr->num_vendor_ext++;
128252726Srpaulo
129252726Srpaulo	return 0;
130252726Srpaulo}
131252726Srpaulo
132252726Srpaulo
133189251Ssamstatic int wps_set_attr(struct wps_parse_attr *attr, u16 type,
134189251Ssam			const u8 *pos, u16 len)
135189251Ssam{
136189251Ssam	switch (type) {
137189251Ssam	case ATTR_VERSION:
138189251Ssam		if (len != 1) {
139189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
140189251Ssam				   len);
141189251Ssam			return -1;
142189251Ssam		}
143189251Ssam		attr->version = pos;
144189251Ssam		break;
145189251Ssam	case ATTR_MSG_TYPE:
146189251Ssam		if (len != 1) {
147189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
148189251Ssam				   "length %u", len);
149189251Ssam			return -1;
150189251Ssam		}
151189251Ssam		attr->msg_type = pos;
152189251Ssam		break;
153189251Ssam	case ATTR_ENROLLEE_NONCE:
154189251Ssam		if (len != WPS_NONCE_LEN) {
155189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
156189251Ssam				   "length %u", len);
157189251Ssam			return -1;
158189251Ssam		}
159189251Ssam		attr->enrollee_nonce = pos;
160189251Ssam		break;
161189251Ssam	case ATTR_REGISTRAR_NONCE:
162189251Ssam		if (len != WPS_NONCE_LEN) {
163189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
164189251Ssam				   "length %u", len);
165189251Ssam			return -1;
166189251Ssam		}
167189251Ssam		attr->registrar_nonce = pos;
168189251Ssam		break;
169189251Ssam	case ATTR_UUID_E:
170189251Ssam		if (len != WPS_UUID_LEN) {
171189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
172189251Ssam				   len);
173189251Ssam			return -1;
174189251Ssam		}
175189251Ssam		attr->uuid_e = pos;
176189251Ssam		break;
177189251Ssam	case ATTR_UUID_R:
178189251Ssam		if (len != WPS_UUID_LEN) {
179189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
180189251Ssam				   len);
181189251Ssam			return -1;
182189251Ssam		}
183189251Ssam		attr->uuid_r = pos;
184189251Ssam		break;
185189251Ssam	case ATTR_AUTH_TYPE_FLAGS:
186189251Ssam		if (len != 2) {
187189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
188189251Ssam				   "Type Flags length %u", len);
189189251Ssam			return -1;
190189251Ssam		}
191189251Ssam		attr->auth_type_flags = pos;
192189251Ssam		break;
193189251Ssam	case ATTR_ENCR_TYPE_FLAGS:
194189251Ssam		if (len != 2) {
195189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
196189251Ssam				   "Flags length %u", len);
197189251Ssam			return -1;
198189251Ssam		}
199189251Ssam		attr->encr_type_flags = pos;
200189251Ssam		break;
201189251Ssam	case ATTR_CONN_TYPE_FLAGS:
202189251Ssam		if (len != 1) {
203189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
204189251Ssam				   "Flags length %u", len);
205189251Ssam			return -1;
206189251Ssam		}
207189251Ssam		attr->conn_type_flags = pos;
208189251Ssam		break;
209189251Ssam	case ATTR_CONFIG_METHODS:
210189251Ssam		if (len != 2) {
211189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
212189251Ssam				   "length %u", len);
213189251Ssam			return -1;
214189251Ssam		}
215189251Ssam		attr->config_methods = pos;
216189251Ssam		break;
217189251Ssam	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
218189251Ssam		if (len != 2) {
219189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
220189251Ssam				   "Registrar Config Methods length %u", len);
221189251Ssam			return -1;
222189251Ssam		}
223189251Ssam		attr->sel_reg_config_methods = pos;
224189251Ssam		break;
225189251Ssam	case ATTR_PRIMARY_DEV_TYPE:
226214734Srpaulo		if (len != WPS_DEV_TYPE_LEN) {
227189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
228189251Ssam				   "Type length %u", len);
229189251Ssam			return -1;
230189251Ssam		}
231189251Ssam		attr->primary_dev_type = pos;
232189251Ssam		break;
233189251Ssam	case ATTR_RF_BANDS:
234189251Ssam		if (len != 1) {
235189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
236189251Ssam				   "%u", len);
237189251Ssam			return -1;
238189251Ssam		}
239189251Ssam		attr->rf_bands = pos;
240189251Ssam		break;
241189251Ssam	case ATTR_ASSOC_STATE:
242189251Ssam		if (len != 2) {
243189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
244189251Ssam				   "length %u", len);
245189251Ssam			return -1;
246189251Ssam		}
247189251Ssam		attr->assoc_state = pos;
248189251Ssam		break;
249189251Ssam	case ATTR_CONFIG_ERROR:
250189251Ssam		if (len != 2) {
251189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
252189251Ssam				   "Error length %u", len);
253189251Ssam			return -1;
254189251Ssam		}
255189251Ssam		attr->config_error = pos;
256189251Ssam		break;
257189251Ssam	case ATTR_DEV_PASSWORD_ID:
258189251Ssam		if (len != 2) {
259189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
260189251Ssam				   "ID length %u", len);
261189251Ssam			return -1;
262189251Ssam		}
263189251Ssam		attr->dev_password_id = pos;
264189251Ssam		break;
265214734Srpaulo	case ATTR_OOB_DEVICE_PASSWORD:
266252726Srpaulo		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
267252726Srpaulo		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
268252726Srpaulo		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
269252726Srpaulo		    WPS_OOB_DEVICE_PASSWORD_LEN) {
270214734Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
271214734Srpaulo				   "Password length %u", len);
272214734Srpaulo			return -1;
273214734Srpaulo		}
274214734Srpaulo		attr->oob_dev_password = pos;
275252726Srpaulo		attr->oob_dev_password_len = len;
276214734Srpaulo		break;
277189251Ssam	case ATTR_OS_VERSION:
278189251Ssam		if (len != 4) {
279189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
280189251Ssam				   "%u", len);
281189251Ssam			return -1;
282189251Ssam		}
283189251Ssam		attr->os_version = pos;
284189251Ssam		break;
285189251Ssam	case ATTR_WPS_STATE:
286189251Ssam		if (len != 1) {
287189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
288189251Ssam				   "Setup State length %u", len);
289189251Ssam			return -1;
290189251Ssam		}
291189251Ssam		attr->wps_state = pos;
292189251Ssam		break;
293189251Ssam	case ATTR_AUTHENTICATOR:
294189251Ssam		if (len != WPS_AUTHENTICATOR_LEN) {
295189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
296189251Ssam				   "length %u", len);
297189251Ssam			return -1;
298189251Ssam		}
299189251Ssam		attr->authenticator = pos;
300189251Ssam		break;
301189251Ssam	case ATTR_R_HASH1:
302189251Ssam		if (len != WPS_HASH_LEN) {
303189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
304189251Ssam				   len);
305189251Ssam			return -1;
306189251Ssam		}
307189251Ssam		attr->r_hash1 = pos;
308189251Ssam		break;
309189251Ssam	case ATTR_R_HASH2:
310189251Ssam		if (len != WPS_HASH_LEN) {
311189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
312189251Ssam				   len);
313189251Ssam			return -1;
314189251Ssam		}
315189251Ssam		attr->r_hash2 = pos;
316189251Ssam		break;
317189251Ssam	case ATTR_E_HASH1:
318189251Ssam		if (len != WPS_HASH_LEN) {
319189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
320189251Ssam				   len);
321189251Ssam			return -1;
322189251Ssam		}
323189251Ssam		attr->e_hash1 = pos;
324189251Ssam		break;
325189251Ssam	case ATTR_E_HASH2:
326189251Ssam		if (len != WPS_HASH_LEN) {
327189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
328189251Ssam				   len);
329189251Ssam			return -1;
330189251Ssam		}
331189251Ssam		attr->e_hash2 = pos;
332189251Ssam		break;
333189251Ssam	case ATTR_R_SNONCE1:
334189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
335189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
336189251Ssam				   "%u", len);
337189251Ssam			return -1;
338189251Ssam		}
339189251Ssam		attr->r_snonce1 = pos;
340189251Ssam		break;
341189251Ssam	case ATTR_R_SNONCE2:
342189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
343189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
344189251Ssam				   "%u", len);
345189251Ssam			return -1;
346189251Ssam		}
347189251Ssam		attr->r_snonce2 = pos;
348189251Ssam		break;
349189251Ssam	case ATTR_E_SNONCE1:
350189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
351189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
352189251Ssam				   "%u", len);
353189251Ssam			return -1;
354189251Ssam		}
355189251Ssam		attr->e_snonce1 = pos;
356189251Ssam		break;
357189251Ssam	case ATTR_E_SNONCE2:
358189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
359189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
360189251Ssam				   "%u", len);
361189251Ssam			return -1;
362189251Ssam		}
363189251Ssam		attr->e_snonce2 = pos;
364189251Ssam		break;
365189251Ssam	case ATTR_KEY_WRAP_AUTH:
366189251Ssam		if (len != WPS_KWA_LEN) {
367189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
368189251Ssam				   "Authenticator length %u", len);
369189251Ssam			return -1;
370189251Ssam		}
371189251Ssam		attr->key_wrap_auth = pos;
372189251Ssam		break;
373189251Ssam	case ATTR_AUTH_TYPE:
374189251Ssam		if (len != 2) {
375189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
376189251Ssam				   "Type length %u", len);
377189251Ssam			return -1;
378189251Ssam		}
379189251Ssam		attr->auth_type = pos;
380189251Ssam		break;
381189251Ssam	case ATTR_ENCR_TYPE:
382189251Ssam		if (len != 2) {
383189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
384189251Ssam				   "Type length %u", len);
385189251Ssam			return -1;
386189251Ssam		}
387189251Ssam		attr->encr_type = pos;
388189251Ssam		break;
389189251Ssam	case ATTR_NETWORK_INDEX:
390189251Ssam		if (len != 1) {
391189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
392189251Ssam				   "length %u", len);
393189251Ssam			return -1;
394189251Ssam		}
395189251Ssam		attr->network_idx = pos;
396189251Ssam		break;
397189251Ssam	case ATTR_NETWORK_KEY_INDEX:
398189251Ssam		if (len != 1) {
399189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
400189251Ssam				   "length %u", len);
401189251Ssam			return -1;
402189251Ssam		}
403189251Ssam		attr->network_key_idx = pos;
404189251Ssam		break;
405189251Ssam	case ATTR_MAC_ADDR:
406189251Ssam		if (len != ETH_ALEN) {
407189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
408189251Ssam				   "length %u", len);
409189251Ssam			return -1;
410189251Ssam		}
411189251Ssam		attr->mac_addr = pos;
412189251Ssam		break;
413189251Ssam	case ATTR_KEY_PROVIDED_AUTO:
414189251Ssam		if (len != 1) {
415189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
416189251Ssam				   "Automatically length %u", len);
417189251Ssam			return -1;
418189251Ssam		}
419189251Ssam		attr->key_prov_auto = pos;
420189251Ssam		break;
421189251Ssam	case ATTR_802_1X_ENABLED:
422189251Ssam		if (len != 1) {
423189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
424189251Ssam				   "length %u", len);
425189251Ssam			return -1;
426189251Ssam		}
427189251Ssam		attr->dot1x_enabled = pos;
428189251Ssam		break;
429189251Ssam	case ATTR_SELECTED_REGISTRAR:
430189251Ssam		if (len != 1) {
431189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
432189251Ssam				   " length %u", len);
433189251Ssam			return -1;
434189251Ssam		}
435189251Ssam		attr->selected_registrar = pos;
436189251Ssam		break;
437189251Ssam	case ATTR_REQUEST_TYPE:
438189251Ssam		if (len != 1) {
439189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
440189251Ssam				   "length %u", len);
441189251Ssam			return -1;
442189251Ssam		}
443189251Ssam		attr->request_type = pos;
444189251Ssam		break;
445189251Ssam	case ATTR_RESPONSE_TYPE:
446189251Ssam		if (len != 1) {
447189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
448189251Ssam				   "length %u", len);
449189251Ssam			return -1;
450189251Ssam		}
451214734Srpaulo		attr->response_type = pos;
452189251Ssam		break;
453189251Ssam	case ATTR_MANUFACTURER:
454189251Ssam		attr->manufacturer = pos;
455189251Ssam		attr->manufacturer_len = len;
456189251Ssam		break;
457189251Ssam	case ATTR_MODEL_NAME:
458189251Ssam		attr->model_name = pos;
459189251Ssam		attr->model_name_len = len;
460189251Ssam		break;
461189251Ssam	case ATTR_MODEL_NUMBER:
462189251Ssam		attr->model_number = pos;
463189251Ssam		attr->model_number_len = len;
464189251Ssam		break;
465189251Ssam	case ATTR_SERIAL_NUMBER:
466189251Ssam		attr->serial_number = pos;
467189251Ssam		attr->serial_number_len = len;
468189251Ssam		break;
469189251Ssam	case ATTR_DEV_NAME:
470189251Ssam		attr->dev_name = pos;
471189251Ssam		attr->dev_name_len = len;
472189251Ssam		break;
473189251Ssam	case ATTR_PUBLIC_KEY:
474189251Ssam		attr->public_key = pos;
475189251Ssam		attr->public_key_len = len;
476189251Ssam		break;
477189251Ssam	case ATTR_ENCR_SETTINGS:
478189251Ssam		attr->encr_settings = pos;
479189251Ssam		attr->encr_settings_len = len;
480189251Ssam		break;
481189251Ssam	case ATTR_CRED:
482189251Ssam		if (attr->num_cred >= MAX_CRED_COUNT) {
483189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
484189251Ssam				   "attribute (max %d credentials)",
485189251Ssam				   MAX_CRED_COUNT);
486189251Ssam			break;
487189251Ssam		}
488189251Ssam		attr->cred[attr->num_cred] = pos;
489189251Ssam		attr->cred_len[attr->num_cred] = len;
490189251Ssam		attr->num_cred++;
491189251Ssam		break;
492189251Ssam	case ATTR_SSID:
493189251Ssam		attr->ssid = pos;
494189251Ssam		attr->ssid_len = len;
495189251Ssam		break;
496189251Ssam	case ATTR_NETWORK_KEY:
497189251Ssam		attr->network_key = pos;
498189251Ssam		attr->network_key_len = len;
499189251Ssam		break;
500189251Ssam	case ATTR_EAP_TYPE:
501189251Ssam		attr->eap_type = pos;
502189251Ssam		attr->eap_type_len = len;
503189251Ssam		break;
504189251Ssam	case ATTR_EAP_IDENTITY:
505189251Ssam		attr->eap_identity = pos;
506189251Ssam		attr->eap_identity_len = len;
507189251Ssam		break;
508209158Srpaulo	case ATTR_AP_SETUP_LOCKED:
509209158Srpaulo		if (len != 1) {
510209158Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
511209158Srpaulo				   "length %u", len);
512209158Srpaulo			return -1;
513209158Srpaulo		}
514209158Srpaulo		attr->ap_setup_locked = pos;
515209158Srpaulo		break;
516252726Srpaulo	case ATTR_REQUESTED_DEV_TYPE:
517252726Srpaulo		if (len != WPS_DEV_TYPE_LEN) {
518252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
519252726Srpaulo				   "Type length %u", len);
520252726Srpaulo			return -1;
521252726Srpaulo		}
522252726Srpaulo		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
523252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
524252726Srpaulo				   "Type attribute (max %u types)",
525252726Srpaulo				   MAX_REQ_DEV_TYPE_COUNT);
526252726Srpaulo			break;
527252726Srpaulo		}
528252726Srpaulo		attr->req_dev_type[attr->num_req_dev_type] = pos;
529252726Srpaulo		attr->num_req_dev_type++;
530252726Srpaulo		break;
531252726Srpaulo	case ATTR_SECONDARY_DEV_TYPE_LIST:
532252726Srpaulo		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
533252726Srpaulo		    (len % WPS_DEV_TYPE_LEN) > 0) {
534252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
535252726Srpaulo				   "Type length %u", len);
536252726Srpaulo			return -1;
537252726Srpaulo		}
538252726Srpaulo		attr->sec_dev_type_list = pos;
539252726Srpaulo		attr->sec_dev_type_list_len = len;
540252726Srpaulo		break;
541252726Srpaulo	case ATTR_VENDOR_EXT:
542252726Srpaulo		if (wps_parse_vendor_ext(attr, pos, len) < 0)
543252726Srpaulo			return -1;
544252726Srpaulo		break;
545252726Srpaulo	case ATTR_AP_CHANNEL:
546252726Srpaulo		if (len != 2) {
547252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
548252726Srpaulo				   "length %u", len);
549252726Srpaulo			return -1;
550252726Srpaulo		}
551252726Srpaulo		attr->ap_channel = pos;
552252726Srpaulo		break;
553189251Ssam	default:
554189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
555189251Ssam			   "len=%u", type, len);
556189251Ssam		break;
557189251Ssam	}
558189251Ssam
559189251Ssam	return 0;
560189251Ssam}
561189251Ssam
562189251Ssam
563189251Ssamint wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
564189251Ssam{
565189251Ssam	const u8 *pos, *end;
566189251Ssam	u16 type, len;
567252726Srpaulo#ifdef WPS_WORKAROUNDS
568252726Srpaulo	u16 prev_type = 0;
569252726Srpaulo#endif /* WPS_WORKAROUNDS */
570189251Ssam
571189251Ssam	os_memset(attr, 0, sizeof(*attr));
572189251Ssam	pos = wpabuf_head(msg);
573189251Ssam	end = pos + wpabuf_len(msg);
574189251Ssam
575189251Ssam	while (pos < end) {
576189251Ssam		if (end - pos < 4) {
577189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
578189251Ssam				   "%lu bytes remaining",
579189251Ssam				   (unsigned long) (end - pos));
580189251Ssam			return -1;
581189251Ssam		}
582189251Ssam
583189251Ssam		type = WPA_GET_BE16(pos);
584189251Ssam		pos += 2;
585189251Ssam		len = WPA_GET_BE16(pos);
586189251Ssam		pos += 2;
587252726Srpaulo		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
588189251Ssam			   type, len);
589189251Ssam		if (len > end - pos) {
590189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
591252726Srpaulo			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
592252726Srpaulo#ifdef WPS_WORKAROUNDS
593252726Srpaulo			/*
594252726Srpaulo			 * Some deployed APs seem to have a bug in encoding of
595252726Srpaulo			 * Network Key attribute in the Credential attribute
596252726Srpaulo			 * where they add an extra octet after the Network Key
597252726Srpaulo			 * attribute at least when open network is being
598252726Srpaulo			 * provisioned.
599252726Srpaulo			 */
600252726Srpaulo			if ((type & 0xff00) != 0x1000 &&
601252726Srpaulo			    prev_type == ATTR_NETWORK_KEY) {
602252726Srpaulo				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
603252726Srpaulo					   "to skip unexpected octet after "
604252726Srpaulo					   "Network Key");
605252726Srpaulo				pos -= 3;
606252726Srpaulo				continue;
607252726Srpaulo			}
608252726Srpaulo#endif /* WPS_WORKAROUNDS */
609189251Ssam			return -1;
610189251Ssam		}
611189251Ssam
612214734Srpaulo#ifdef WPS_WORKAROUNDS
613214734Srpaulo		if (type == 0 && len == 0) {
614214734Srpaulo			/*
615214734Srpaulo			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
616214734Srpaulo			 * end of M1. Skip those to avoid interop issues.
617214734Srpaulo			 */
618214734Srpaulo			int i;
619214734Srpaulo			for (i = 0; i < end - pos; i++) {
620214734Srpaulo				if (pos[i])
621214734Srpaulo					break;
622214734Srpaulo			}
623214734Srpaulo			if (i == end - pos) {
624214734Srpaulo				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
625214734Srpaulo					   "unexpected message padding");
626214734Srpaulo				break;
627214734Srpaulo			}
628214734Srpaulo		}
629214734Srpaulo#endif /* WPS_WORKAROUNDS */
630214734Srpaulo
631189251Ssam		if (wps_set_attr(attr, type, pos, len) < 0)
632189251Ssam			return -1;
633189251Ssam
634252726Srpaulo#ifdef WPS_WORKAROUNDS
635252726Srpaulo		prev_type = type;
636252726Srpaulo#endif /* WPS_WORKAROUNDS */
637189251Ssam		pos += len;
638189251Ssam	}
639189251Ssam
640189251Ssam	return 0;
641189251Ssam}
642