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