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#define WPS_WORKAROUNDS
21
22
23static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
24			const u8 *pos, u16 len)
25{
26	switch (type) {
27	case ATTR_VERSION:
28		if (len != 1) {
29			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
30				   len);
31			return -1;
32		}
33		attr->version = pos;
34		break;
35	case ATTR_MSG_TYPE:
36		if (len != 1) {
37			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
38				   "length %u", len);
39			return -1;
40		}
41		attr->msg_type = pos;
42		break;
43	case ATTR_ENROLLEE_NONCE:
44		if (len != WPS_NONCE_LEN) {
45			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
46				   "length %u", len);
47			return -1;
48		}
49		attr->enrollee_nonce = pos;
50		break;
51	case ATTR_REGISTRAR_NONCE:
52		if (len != WPS_NONCE_LEN) {
53			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
54				   "length %u", len);
55			return -1;
56		}
57		attr->registrar_nonce = pos;
58		break;
59	case ATTR_UUID_E:
60		if (len != WPS_UUID_LEN) {
61			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
62				   len);
63			return -1;
64		}
65		attr->uuid_e = pos;
66		break;
67	case ATTR_UUID_R:
68		if (len != WPS_UUID_LEN) {
69			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
70				   len);
71			return -1;
72		}
73		attr->uuid_r = pos;
74		break;
75	case ATTR_AUTH_TYPE_FLAGS:
76		if (len != 2) {
77			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
78				   "Type Flags length %u", len);
79			return -1;
80		}
81		attr->auth_type_flags = pos;
82		break;
83	case ATTR_ENCR_TYPE_FLAGS:
84		if (len != 2) {
85			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
86				   "Flags length %u", len);
87			return -1;
88		}
89		attr->encr_type_flags = pos;
90		break;
91	case ATTR_CONN_TYPE_FLAGS:
92		if (len != 1) {
93			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
94				   "Flags length %u", len);
95			return -1;
96		}
97		attr->conn_type_flags = pos;
98		break;
99	case ATTR_CONFIG_METHODS:
100		if (len != 2) {
101			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
102				   "length %u", len);
103			return -1;
104		}
105		attr->config_methods = pos;
106		break;
107	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
108		if (len != 2) {
109			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
110				   "Registrar Config Methods length %u", len);
111			return -1;
112		}
113		attr->sel_reg_config_methods = pos;
114		break;
115	case ATTR_PRIMARY_DEV_TYPE:
116		if (len != WPS_DEV_TYPE_LEN) {
117			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
118				   "Type length %u", len);
119			return -1;
120		}
121		attr->primary_dev_type = pos;
122		break;
123	case ATTR_RF_BANDS:
124		if (len != 1) {
125			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
126				   "%u", len);
127			return -1;
128		}
129		attr->rf_bands = pos;
130		break;
131	case ATTR_ASSOC_STATE:
132		if (len != 2) {
133			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
134				   "length %u", len);
135			return -1;
136		}
137		attr->assoc_state = pos;
138		break;
139	case ATTR_CONFIG_ERROR:
140		if (len != 2) {
141			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
142				   "Error length %u", len);
143			return -1;
144		}
145		attr->config_error = pos;
146		break;
147	case ATTR_DEV_PASSWORD_ID:
148		if (len != 2) {
149			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
150				   "ID length %u", len);
151			return -1;
152		}
153		attr->dev_password_id = pos;
154		break;
155	case ATTR_OOB_DEVICE_PASSWORD:
156		if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
157			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
158				   "Password length %u", len);
159			return -1;
160		}
161		attr->oob_dev_password = pos;
162		break;
163	case ATTR_OS_VERSION:
164		if (len != 4) {
165			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
166				   "%u", len);
167			return -1;
168		}
169		attr->os_version = pos;
170		break;
171	case ATTR_WPS_STATE:
172		if (len != 1) {
173			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
174				   "Setup State length %u", len);
175			return -1;
176		}
177		attr->wps_state = pos;
178		break;
179	case ATTR_AUTHENTICATOR:
180		if (len != WPS_AUTHENTICATOR_LEN) {
181			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
182				   "length %u", len);
183			return -1;
184		}
185		attr->authenticator = pos;
186		break;
187	case ATTR_R_HASH1:
188		if (len != WPS_HASH_LEN) {
189			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
190				   len);
191			return -1;
192		}
193		attr->r_hash1 = pos;
194		break;
195	case ATTR_R_HASH2:
196		if (len != WPS_HASH_LEN) {
197			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
198				   len);
199			return -1;
200		}
201		attr->r_hash2 = pos;
202		break;
203	case ATTR_E_HASH1:
204		if (len != WPS_HASH_LEN) {
205			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
206				   len);
207			return -1;
208		}
209		attr->e_hash1 = pos;
210		break;
211	case ATTR_E_HASH2:
212		if (len != WPS_HASH_LEN) {
213			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
214				   len);
215			return -1;
216		}
217		attr->e_hash2 = pos;
218		break;
219	case ATTR_R_SNONCE1:
220		if (len != WPS_SECRET_NONCE_LEN) {
221			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
222				   "%u", len);
223			return -1;
224		}
225		attr->r_snonce1 = pos;
226		break;
227	case ATTR_R_SNONCE2:
228		if (len != WPS_SECRET_NONCE_LEN) {
229			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
230				   "%u", len);
231			return -1;
232		}
233		attr->r_snonce2 = pos;
234		break;
235	case ATTR_E_SNONCE1:
236		if (len != WPS_SECRET_NONCE_LEN) {
237			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
238				   "%u", len);
239			return -1;
240		}
241		attr->e_snonce1 = pos;
242		break;
243	case ATTR_E_SNONCE2:
244		if (len != WPS_SECRET_NONCE_LEN) {
245			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
246				   "%u", len);
247			return -1;
248		}
249		attr->e_snonce2 = pos;
250		break;
251	case ATTR_KEY_WRAP_AUTH:
252		if (len != WPS_KWA_LEN) {
253			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
254				   "Authenticator length %u", len);
255			return -1;
256		}
257		attr->key_wrap_auth = pos;
258		break;
259	case ATTR_AUTH_TYPE:
260		if (len != 2) {
261			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
262				   "Type length %u", len);
263			return -1;
264		}
265		attr->auth_type = pos;
266		break;
267	case ATTR_ENCR_TYPE:
268		if (len != 2) {
269			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
270				   "Type length %u", len);
271			return -1;
272		}
273		attr->encr_type = pos;
274		break;
275	case ATTR_NETWORK_INDEX:
276		if (len != 1) {
277			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
278				   "length %u", len);
279			return -1;
280		}
281		attr->network_idx = pos;
282		break;
283	case ATTR_NETWORK_KEY_INDEX:
284		if (len != 1) {
285			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
286				   "length %u", len);
287			return -1;
288		}
289		attr->network_key_idx = pos;
290		break;
291	case ATTR_MAC_ADDR:
292		if (len != ETH_ALEN) {
293			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
294				   "length %u", len);
295			return -1;
296		}
297		attr->mac_addr = pos;
298		break;
299	case ATTR_KEY_PROVIDED_AUTO:
300		if (len != 1) {
301			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
302				   "Automatically length %u", len);
303			return -1;
304		}
305		attr->key_prov_auto = pos;
306		break;
307	case ATTR_802_1X_ENABLED:
308		if (len != 1) {
309			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
310				   "length %u", len);
311			return -1;
312		}
313		attr->dot1x_enabled = pos;
314		break;
315	case ATTR_SELECTED_REGISTRAR:
316		if (len != 1) {
317			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
318				   " length %u", len);
319			return -1;
320		}
321		attr->selected_registrar = pos;
322		break;
323	case ATTR_REQUEST_TYPE:
324		if (len != 1) {
325			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
326				   "length %u", len);
327			return -1;
328		}
329		attr->request_type = pos;
330		break;
331	case ATTR_RESPONSE_TYPE:
332		if (len != 1) {
333			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
334				   "length %u", len);
335			return -1;
336		}
337		attr->response_type = pos;
338		break;
339	case ATTR_MANUFACTURER:
340		attr->manufacturer = pos;
341		attr->manufacturer_len = len;
342		break;
343	case ATTR_MODEL_NAME:
344		attr->model_name = pos;
345		attr->model_name_len = len;
346		break;
347	case ATTR_MODEL_NUMBER:
348		attr->model_number = pos;
349		attr->model_number_len = len;
350		break;
351	case ATTR_SERIAL_NUMBER:
352		attr->serial_number = pos;
353		attr->serial_number_len = len;
354		break;
355	case ATTR_DEV_NAME:
356		attr->dev_name = pos;
357		attr->dev_name_len = len;
358		break;
359	case ATTR_PUBLIC_KEY:
360		attr->public_key = pos;
361		attr->public_key_len = len;
362		break;
363	case ATTR_ENCR_SETTINGS:
364		attr->encr_settings = pos;
365		attr->encr_settings_len = len;
366		break;
367	case ATTR_CRED:
368		if (attr->num_cred >= MAX_CRED_COUNT) {
369			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
370				   "attribute (max %d credentials)",
371				   MAX_CRED_COUNT);
372			break;
373		}
374		attr->cred[attr->num_cred] = pos;
375		attr->cred_len[attr->num_cred] = len;
376		attr->num_cred++;
377		break;
378	case ATTR_SSID:
379		attr->ssid = pos;
380		attr->ssid_len = len;
381		break;
382	case ATTR_NETWORK_KEY:
383		attr->network_key = pos;
384		attr->network_key_len = len;
385		break;
386	case ATTR_EAP_TYPE:
387		attr->eap_type = pos;
388		attr->eap_type_len = len;
389		break;
390	case ATTR_EAP_IDENTITY:
391		attr->eap_identity = pos;
392		attr->eap_identity_len = len;
393		break;
394	case ATTR_AP_SETUP_LOCKED:
395		if (len != 1) {
396			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
397				   "length %u", len);
398			return -1;
399		}
400		attr->ap_setup_locked = pos;
401		break;
402	default:
403		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
404			   "len=%u", type, len);
405		break;
406	}
407
408	return 0;
409}
410
411
412int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
413{
414	const u8 *pos, *end;
415	u16 type, len;
416
417	os_memset(attr, 0, sizeof(*attr));
418	pos = wpabuf_head(msg);
419	end = pos + wpabuf_len(msg);
420
421	while (pos < end) {
422		if (end - pos < 4) {
423			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
424				   "%lu bytes remaining",
425				   (unsigned long) (end - pos));
426			return -1;
427		}
428
429		type = WPA_GET_BE16(pos);
430		pos += 2;
431		len = WPA_GET_BE16(pos);
432		pos += 2;
433		wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
434			   type, len);
435		if (len > end - pos) {
436			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
437			return -1;
438		}
439
440#ifdef WPS_WORKAROUNDS
441		if (type == 0 && len == 0) {
442			/*
443			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
444			 * end of M1. Skip those to avoid interop issues.
445			 */
446			int i;
447			for (i = 0; i < end - pos; i++) {
448				if (pos[i])
449					break;
450			}
451			if (i == end - pos) {
452				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
453					   "unexpected message padding");
454				break;
455			}
456		}
457#endif /* WPS_WORKAROUNDS */
458
459		if (wps_set_attr(attr, type, pos, len) < 0)
460			return -1;
461
462		pos += len;
463	}
464
465	return 0;
466}
467