1/*
2 * Wi-Fi Protected Setup - attribute parsing
3 * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "wps_defs.h"
13#include "wps_attr_parse.h"
14
15#ifndef CONFIG_WPS_STRICT
16#define WPS_WORKAROUNDS
17#endif /* CONFIG_WPS_STRICT */
18
19
20static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
21					  u8 id, u8 len, const u8 *pos)
22{
23	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
24		   id, len);
25	switch (id) {
26	case WFA_ELEM_VERSION2:
27		if (len != 1) {
28			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
29				   "%u", len);
30			return -1;
31		}
32		attr->version2 = pos;
33		break;
34	case WFA_ELEM_AUTHORIZEDMACS:
35		attr->authorized_macs = pos;
36		attr->authorized_macs_len = len;
37		break;
38	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
39		if (len != 1) {
40			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
41				   "Shareable length %u", len);
42			return -1;
43		}
44		attr->network_key_shareable = pos;
45		break;
46	case WFA_ELEM_REQUEST_TO_ENROLL:
47		if (len != 1) {
48			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
49				   "length %u", len);
50			return -1;
51		}
52		attr->request_to_enroll = pos;
53		break;
54	case WFA_ELEM_SETTINGS_DELAY_TIME:
55		if (len != 1) {
56			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
57				   "Time length %u", len);
58			return -1;
59		}
60		attr->settings_delay_time = pos;
61		break;
62	default:
63		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
64			   "Extension subelement %u", id);
65		break;
66	}
67
68	return 0;
69}
70
71
72static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
73				    u16 len)
74{
75	const u8 *end = pos + len;
76	u8 id, elen;
77
78	while (pos + 2 < end) {
79		id = *pos++;
80		elen = *pos++;
81		if (pos + elen > end)
82			break;
83		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
84			return -1;
85		pos += elen;
86	}
87
88	return 0;
89}
90
91
92static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
93				u16 len)
94{
95	u32 vendor_id;
96
97	if (len < 3) {
98		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
99		return 0;
100	}
101
102	vendor_id = WPA_GET_BE24(pos);
103	switch (vendor_id) {
104	case WPS_VENDOR_ID_WFA:
105		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
106	}
107
108	/* Handle unknown vendor extensions */
109
110	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
111		   vendor_id);
112
113	if (len > WPS_MAX_VENDOR_EXT_LEN) {
114		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
115			   len);
116		return -1;
117	}
118
119	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
120		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
121			   "attribute (max %d vendor extensions)",
122			   MAX_WPS_PARSE_VENDOR_EXT);
123		return -1;
124	}
125	attr->vendor_ext[attr->num_vendor_ext] = pos;
126	attr->vendor_ext_len[attr->num_vendor_ext] = len;
127	attr->num_vendor_ext++;
128
129	return 0;
130}
131
132
133static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
134			const u8 *pos, u16 len)
135{
136	switch (type) {
137	case ATTR_VERSION:
138		if (len != 1) {
139			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
140				   len);
141			return -1;
142		}
143		attr->version = pos;
144		break;
145	case ATTR_MSG_TYPE:
146		if (len != 1) {
147			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
148				   "length %u", len);
149			return -1;
150		}
151		attr->msg_type = pos;
152		break;
153	case ATTR_ENROLLEE_NONCE:
154		if (len != WPS_NONCE_LEN) {
155			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
156				   "length %u", len);
157			return -1;
158		}
159		attr->enrollee_nonce = pos;
160		break;
161	case ATTR_REGISTRAR_NONCE:
162		if (len != WPS_NONCE_LEN) {
163			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
164				   "length %u", len);
165			return -1;
166		}
167		attr->registrar_nonce = pos;
168		break;
169	case ATTR_UUID_E:
170		if (len != WPS_UUID_LEN) {
171			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
172				   len);
173			return -1;
174		}
175		attr->uuid_e = pos;
176		break;
177	case ATTR_UUID_R:
178		if (len != WPS_UUID_LEN) {
179			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
180				   len);
181			return -1;
182		}
183		attr->uuid_r = pos;
184		break;
185	case ATTR_AUTH_TYPE_FLAGS:
186		if (len != 2) {
187			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
188				   "Type Flags length %u", len);
189			return -1;
190		}
191		attr->auth_type_flags = pos;
192		break;
193	case ATTR_ENCR_TYPE_FLAGS:
194		if (len != 2) {
195			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
196				   "Flags length %u", len);
197			return -1;
198		}
199		attr->encr_type_flags = pos;
200		break;
201	case ATTR_CONN_TYPE_FLAGS:
202		if (len != 1) {
203			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
204				   "Flags length %u", len);
205			return -1;
206		}
207		attr->conn_type_flags = pos;
208		break;
209	case ATTR_CONFIG_METHODS:
210		if (len != 2) {
211			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
212				   "length %u", len);
213			return -1;
214		}
215		attr->config_methods = pos;
216		break;
217	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
218		if (len != 2) {
219			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
220				   "Registrar Config Methods length %u", len);
221			return -1;
222		}
223		attr->sel_reg_config_methods = pos;
224		break;
225	case ATTR_PRIMARY_DEV_TYPE:
226		if (len != WPS_DEV_TYPE_LEN) {
227			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
228				   "Type length %u", len);
229			return -1;
230		}
231		attr->primary_dev_type = pos;
232		break;
233	case ATTR_RF_BANDS:
234		if (len != 1) {
235			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
236				   "%u", len);
237			return -1;
238		}
239		attr->rf_bands = pos;
240		break;
241	case ATTR_ASSOC_STATE:
242		if (len != 2) {
243			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
244				   "length %u", len);
245			return -1;
246		}
247		attr->assoc_state = pos;
248		break;
249	case ATTR_CONFIG_ERROR:
250		if (len != 2) {
251			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
252				   "Error length %u", len);
253			return -1;
254		}
255		attr->config_error = pos;
256		break;
257	case ATTR_DEV_PASSWORD_ID:
258		if (len != 2) {
259			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
260				   "ID length %u", len);
261			return -1;
262		}
263		attr->dev_password_id = pos;
264		break;
265	case ATTR_OOB_DEVICE_PASSWORD:
266		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
267		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
268		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
269		    WPS_OOB_DEVICE_PASSWORD_LEN) {
270			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
271				   "Password length %u", len);
272			return -1;
273		}
274		attr->oob_dev_password = pos;
275		attr->oob_dev_password_len = len;
276		break;
277	case ATTR_OS_VERSION:
278		if (len != 4) {
279			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
280				   "%u", len);
281			return -1;
282		}
283		attr->os_version = pos;
284		break;
285	case ATTR_WPS_STATE:
286		if (len != 1) {
287			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
288				   "Setup State length %u", len);
289			return -1;
290		}
291		attr->wps_state = pos;
292		break;
293	case ATTR_AUTHENTICATOR:
294		if (len != WPS_AUTHENTICATOR_LEN) {
295			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
296				   "length %u", len);
297			return -1;
298		}
299		attr->authenticator = pos;
300		break;
301	case ATTR_R_HASH1:
302		if (len != WPS_HASH_LEN) {
303			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
304				   len);
305			return -1;
306		}
307		attr->r_hash1 = pos;
308		break;
309	case ATTR_R_HASH2:
310		if (len != WPS_HASH_LEN) {
311			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
312				   len);
313			return -1;
314		}
315		attr->r_hash2 = pos;
316		break;
317	case ATTR_E_HASH1:
318		if (len != WPS_HASH_LEN) {
319			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
320				   len);
321			return -1;
322		}
323		attr->e_hash1 = pos;
324		break;
325	case ATTR_E_HASH2:
326		if (len != WPS_HASH_LEN) {
327			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
328				   len);
329			return -1;
330		}
331		attr->e_hash2 = pos;
332		break;
333	case ATTR_R_SNONCE1:
334		if (len != WPS_SECRET_NONCE_LEN) {
335			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
336				   "%u", len);
337			return -1;
338		}
339		attr->r_snonce1 = pos;
340		break;
341	case ATTR_R_SNONCE2:
342		if (len != WPS_SECRET_NONCE_LEN) {
343			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
344				   "%u", len);
345			return -1;
346		}
347		attr->r_snonce2 = pos;
348		break;
349	case ATTR_E_SNONCE1:
350		if (len != WPS_SECRET_NONCE_LEN) {
351			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
352				   "%u", len);
353			return -1;
354		}
355		attr->e_snonce1 = pos;
356		break;
357	case ATTR_E_SNONCE2:
358		if (len != WPS_SECRET_NONCE_LEN) {
359			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
360				   "%u", len);
361			return -1;
362		}
363		attr->e_snonce2 = pos;
364		break;
365	case ATTR_KEY_WRAP_AUTH:
366		if (len != WPS_KWA_LEN) {
367			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
368				   "Authenticator length %u", len);
369			return -1;
370		}
371		attr->key_wrap_auth = pos;
372		break;
373	case ATTR_AUTH_TYPE:
374		if (len != 2) {
375			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
376				   "Type length %u", len);
377			return -1;
378		}
379		attr->auth_type = pos;
380		break;
381	case ATTR_ENCR_TYPE:
382		if (len != 2) {
383			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
384				   "Type length %u", len);
385			return -1;
386		}
387		attr->encr_type = pos;
388		break;
389	case ATTR_NETWORK_INDEX:
390		if (len != 1) {
391			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
392				   "length %u", len);
393			return -1;
394		}
395		attr->network_idx = pos;
396		break;
397	case ATTR_NETWORK_KEY_INDEX:
398		if (len != 1) {
399			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
400				   "length %u", len);
401			return -1;
402		}
403		attr->network_key_idx = pos;
404		break;
405	case ATTR_MAC_ADDR:
406		if (len != ETH_ALEN) {
407			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
408				   "length %u", len);
409			return -1;
410		}
411		attr->mac_addr = pos;
412		break;
413	case ATTR_KEY_PROVIDED_AUTO:
414		if (len != 1) {
415			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
416				   "Automatically length %u", len);
417			return -1;
418		}
419		attr->key_prov_auto = pos;
420		break;
421	case ATTR_802_1X_ENABLED:
422		if (len != 1) {
423			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
424				   "length %u", len);
425			return -1;
426		}
427		attr->dot1x_enabled = pos;
428		break;
429	case ATTR_SELECTED_REGISTRAR:
430		if (len != 1) {
431			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
432				   " length %u", len);
433			return -1;
434		}
435		attr->selected_registrar = pos;
436		break;
437	case ATTR_REQUEST_TYPE:
438		if (len != 1) {
439			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
440				   "length %u", len);
441			return -1;
442		}
443		attr->request_type = pos;
444		break;
445	case ATTR_RESPONSE_TYPE:
446		if (len != 1) {
447			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
448				   "length %u", len);
449			return -1;
450		}
451		attr->response_type = pos;
452		break;
453	case ATTR_MANUFACTURER:
454		attr->manufacturer = pos;
455		attr->manufacturer_len = len;
456		break;
457	case ATTR_MODEL_NAME:
458		attr->model_name = pos;
459		attr->model_name_len = len;
460		break;
461	case ATTR_MODEL_NUMBER:
462		attr->model_number = pos;
463		attr->model_number_len = len;
464		break;
465	case ATTR_SERIAL_NUMBER:
466		attr->serial_number = pos;
467		attr->serial_number_len = len;
468		break;
469	case ATTR_DEV_NAME:
470		attr->dev_name = pos;
471		attr->dev_name_len = len;
472		break;
473	case ATTR_PUBLIC_KEY:
474		attr->public_key = pos;
475		attr->public_key_len = len;
476		break;
477	case ATTR_ENCR_SETTINGS:
478		attr->encr_settings = pos;
479		attr->encr_settings_len = len;
480		break;
481	case ATTR_CRED:
482		if (attr->num_cred >= MAX_CRED_COUNT) {
483			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
484				   "attribute (max %d credentials)",
485				   MAX_CRED_COUNT);
486			break;
487		}
488		attr->cred[attr->num_cred] = pos;
489		attr->cred_len[attr->num_cred] = len;
490		attr->num_cred++;
491		break;
492	case ATTR_SSID:
493		attr->ssid = pos;
494		attr->ssid_len = len;
495		break;
496	case ATTR_NETWORK_KEY:
497		attr->network_key = pos;
498		attr->network_key_len = len;
499		break;
500	case ATTR_EAP_TYPE:
501		attr->eap_type = pos;
502		attr->eap_type_len = len;
503		break;
504	case ATTR_EAP_IDENTITY:
505		attr->eap_identity = pos;
506		attr->eap_identity_len = len;
507		break;
508	case ATTR_AP_SETUP_LOCKED:
509		if (len != 1) {
510			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
511				   "length %u", len);
512			return -1;
513		}
514		attr->ap_setup_locked = pos;
515		break;
516	case ATTR_REQUESTED_DEV_TYPE:
517		if (len != WPS_DEV_TYPE_LEN) {
518			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
519				   "Type length %u", len);
520			return -1;
521		}
522		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
523			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
524				   "Type attribute (max %u types)",
525				   MAX_REQ_DEV_TYPE_COUNT);
526			break;
527		}
528		attr->req_dev_type[attr->num_req_dev_type] = pos;
529		attr->num_req_dev_type++;
530		break;
531	case ATTR_SECONDARY_DEV_TYPE_LIST:
532		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
533		    (len % WPS_DEV_TYPE_LEN) > 0) {
534			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
535				   "Type length %u", len);
536			return -1;
537		}
538		attr->sec_dev_type_list = pos;
539		attr->sec_dev_type_list_len = len;
540		break;
541	case ATTR_VENDOR_EXT:
542		if (wps_parse_vendor_ext(attr, pos, len) < 0)
543			return -1;
544		break;
545	case ATTR_AP_CHANNEL:
546		if (len != 2) {
547			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
548				   "length %u", len);
549			return -1;
550		}
551		attr->ap_channel = pos;
552		break;
553	default:
554		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
555			   "len=%u", type, len);
556		break;
557	}
558
559	return 0;
560}
561
562
563int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
564{
565	const u8 *pos, *end;
566	u16 type, len;
567#ifdef WPS_WORKAROUNDS
568	u16 prev_type = 0;
569#endif /* WPS_WORKAROUNDS */
570
571	os_memset(attr, 0, sizeof(*attr));
572	pos = wpabuf_head(msg);
573	end = pos + wpabuf_len(msg);
574
575	while (pos < end) {
576		if (end - pos < 4) {
577			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
578				   "%lu bytes remaining",
579				   (unsigned long) (end - pos));
580			return -1;
581		}
582
583		type = WPA_GET_BE16(pos);
584		pos += 2;
585		len = WPA_GET_BE16(pos);
586		pos += 2;
587		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
588			   type, len);
589		if (len > end - pos) {
590			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
591			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
592#ifdef WPS_WORKAROUNDS
593			/*
594			 * Some deployed APs seem to have a bug in encoding of
595			 * Network Key attribute in the Credential attribute
596			 * where they add an extra octet after the Network Key
597			 * attribute at least when open network is being
598			 * provisioned.
599			 */
600			if ((type & 0xff00) != 0x1000 &&
601			    prev_type == ATTR_NETWORK_KEY) {
602				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
603					   "to skip unexpected octet after "
604					   "Network Key");
605				pos -= 3;
606				continue;
607			}
608#endif /* WPS_WORKAROUNDS */
609			return -1;
610		}
611
612#ifdef WPS_WORKAROUNDS
613		if (type == 0 && len == 0) {
614			/*
615			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
616			 * end of M1. Skip those to avoid interop issues.
617			 */
618			int i;
619			for (i = 0; i < end - pos; i++) {
620				if (pos[i])
621					break;
622			}
623			if (i == end - pos) {
624				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
625					   "unexpected message padding");
626				break;
627			}
628		}
629#endif /* WPS_WORKAROUNDS */
630
631		if (wps_set_attr(attr, type, pos, len) < 0)
632			return -1;
633
634#ifdef WPS_WORKAROUNDS
635		prev_type = type;
636#endif /* WPS_WORKAROUNDS */
637		pos += len;
638	}
639
640	return 0;
641}
642