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