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