1189251Ssam/*
2189251Ssam * Wi-Fi Protected Setup - attribute parsing
3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#include "common.h"
18189251Ssam#include "wps_i.h"
19189251Ssam
20214734Srpaulo#define WPS_WORKAROUNDS
21189251Ssam
22214734Srpaulo
23189251Ssamstatic int wps_set_attr(struct wps_parse_attr *attr, u16 type,
24189251Ssam			const u8 *pos, u16 len)
25189251Ssam{
26189251Ssam	switch (type) {
27189251Ssam	case ATTR_VERSION:
28189251Ssam		if (len != 1) {
29189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
30189251Ssam				   len);
31189251Ssam			return -1;
32189251Ssam		}
33189251Ssam		attr->version = pos;
34189251Ssam		break;
35189251Ssam	case ATTR_MSG_TYPE:
36189251Ssam		if (len != 1) {
37189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
38189251Ssam				   "length %u", len);
39189251Ssam			return -1;
40189251Ssam		}
41189251Ssam		attr->msg_type = pos;
42189251Ssam		break;
43189251Ssam	case ATTR_ENROLLEE_NONCE:
44189251Ssam		if (len != WPS_NONCE_LEN) {
45189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
46189251Ssam				   "length %u", len);
47189251Ssam			return -1;
48189251Ssam		}
49189251Ssam		attr->enrollee_nonce = pos;
50189251Ssam		break;
51189251Ssam	case ATTR_REGISTRAR_NONCE:
52189251Ssam		if (len != WPS_NONCE_LEN) {
53189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
54189251Ssam				   "length %u", len);
55189251Ssam			return -1;
56189251Ssam		}
57189251Ssam		attr->registrar_nonce = pos;
58189251Ssam		break;
59189251Ssam	case ATTR_UUID_E:
60189251Ssam		if (len != WPS_UUID_LEN) {
61189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
62189251Ssam				   len);
63189251Ssam			return -1;
64189251Ssam		}
65189251Ssam		attr->uuid_e = pos;
66189251Ssam		break;
67189251Ssam	case ATTR_UUID_R:
68189251Ssam		if (len != WPS_UUID_LEN) {
69189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
70189251Ssam				   len);
71189251Ssam			return -1;
72189251Ssam		}
73189251Ssam		attr->uuid_r = pos;
74189251Ssam		break;
75189251Ssam	case ATTR_AUTH_TYPE_FLAGS:
76189251Ssam		if (len != 2) {
77189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
78189251Ssam				   "Type Flags length %u", len);
79189251Ssam			return -1;
80189251Ssam		}
81189251Ssam		attr->auth_type_flags = pos;
82189251Ssam		break;
83189251Ssam	case ATTR_ENCR_TYPE_FLAGS:
84189251Ssam		if (len != 2) {
85189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
86189251Ssam				   "Flags length %u", len);
87189251Ssam			return -1;
88189251Ssam		}
89189251Ssam		attr->encr_type_flags = pos;
90189251Ssam		break;
91189251Ssam	case ATTR_CONN_TYPE_FLAGS:
92189251Ssam		if (len != 1) {
93189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
94189251Ssam				   "Flags length %u", len);
95189251Ssam			return -1;
96189251Ssam		}
97189251Ssam		attr->conn_type_flags = pos;
98189251Ssam		break;
99189251Ssam	case ATTR_CONFIG_METHODS:
100189251Ssam		if (len != 2) {
101189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
102189251Ssam				   "length %u", len);
103189251Ssam			return -1;
104189251Ssam		}
105189251Ssam		attr->config_methods = pos;
106189251Ssam		break;
107189251Ssam	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
108189251Ssam		if (len != 2) {
109189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
110189251Ssam				   "Registrar Config Methods length %u", len);
111189251Ssam			return -1;
112189251Ssam		}
113189251Ssam		attr->sel_reg_config_methods = pos;
114189251Ssam		break;
115189251Ssam	case ATTR_PRIMARY_DEV_TYPE:
116214734Srpaulo		if (len != WPS_DEV_TYPE_LEN) {
117189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
118189251Ssam				   "Type length %u", len);
119189251Ssam			return -1;
120189251Ssam		}
121189251Ssam		attr->primary_dev_type = pos;
122189251Ssam		break;
123189251Ssam	case ATTR_RF_BANDS:
124189251Ssam		if (len != 1) {
125189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
126189251Ssam				   "%u", len);
127189251Ssam			return -1;
128189251Ssam		}
129189251Ssam		attr->rf_bands = pos;
130189251Ssam		break;
131189251Ssam	case ATTR_ASSOC_STATE:
132189251Ssam		if (len != 2) {
133189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
134189251Ssam				   "length %u", len);
135189251Ssam			return -1;
136189251Ssam		}
137189251Ssam		attr->assoc_state = pos;
138189251Ssam		break;
139189251Ssam	case ATTR_CONFIG_ERROR:
140189251Ssam		if (len != 2) {
141189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
142189251Ssam				   "Error length %u", len);
143189251Ssam			return -1;
144189251Ssam		}
145189251Ssam		attr->config_error = pos;
146189251Ssam		break;
147189251Ssam	case ATTR_DEV_PASSWORD_ID:
148189251Ssam		if (len != 2) {
149189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
150189251Ssam				   "ID length %u", len);
151189251Ssam			return -1;
152189251Ssam		}
153189251Ssam		attr->dev_password_id = pos;
154189251Ssam		break;
155214734Srpaulo	case ATTR_OOB_DEVICE_PASSWORD:
156214734Srpaulo		if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
157214734Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
158214734Srpaulo				   "Password length %u", len);
159214734Srpaulo			return -1;
160214734Srpaulo		}
161214734Srpaulo		attr->oob_dev_password = pos;
162214734Srpaulo		break;
163189251Ssam	case ATTR_OS_VERSION:
164189251Ssam		if (len != 4) {
165189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
166189251Ssam				   "%u", len);
167189251Ssam			return -1;
168189251Ssam		}
169189251Ssam		attr->os_version = pos;
170189251Ssam		break;
171189251Ssam	case ATTR_WPS_STATE:
172189251Ssam		if (len != 1) {
173189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
174189251Ssam				   "Setup State length %u", len);
175189251Ssam			return -1;
176189251Ssam		}
177189251Ssam		attr->wps_state = pos;
178189251Ssam		break;
179189251Ssam	case ATTR_AUTHENTICATOR:
180189251Ssam		if (len != WPS_AUTHENTICATOR_LEN) {
181189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
182189251Ssam				   "length %u", len);
183189251Ssam			return -1;
184189251Ssam		}
185189251Ssam		attr->authenticator = pos;
186189251Ssam		break;
187189251Ssam	case ATTR_R_HASH1:
188189251Ssam		if (len != WPS_HASH_LEN) {
189189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
190189251Ssam				   len);
191189251Ssam			return -1;
192189251Ssam		}
193189251Ssam		attr->r_hash1 = pos;
194189251Ssam		break;
195189251Ssam	case ATTR_R_HASH2:
196189251Ssam		if (len != WPS_HASH_LEN) {
197189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
198189251Ssam				   len);
199189251Ssam			return -1;
200189251Ssam		}
201189251Ssam		attr->r_hash2 = pos;
202189251Ssam		break;
203189251Ssam	case ATTR_E_HASH1:
204189251Ssam		if (len != WPS_HASH_LEN) {
205189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
206189251Ssam				   len);
207189251Ssam			return -1;
208189251Ssam		}
209189251Ssam		attr->e_hash1 = pos;
210189251Ssam		break;
211189251Ssam	case ATTR_E_HASH2:
212189251Ssam		if (len != WPS_HASH_LEN) {
213189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
214189251Ssam				   len);
215189251Ssam			return -1;
216189251Ssam		}
217189251Ssam		attr->e_hash2 = pos;
218189251Ssam		break;
219189251Ssam	case ATTR_R_SNONCE1:
220189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
221189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
222189251Ssam				   "%u", len);
223189251Ssam			return -1;
224189251Ssam		}
225189251Ssam		attr->r_snonce1 = pos;
226189251Ssam		break;
227189251Ssam	case ATTR_R_SNONCE2:
228189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
229189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
230189251Ssam				   "%u", len);
231189251Ssam			return -1;
232189251Ssam		}
233189251Ssam		attr->r_snonce2 = pos;
234189251Ssam		break;
235189251Ssam	case ATTR_E_SNONCE1:
236189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
237189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
238189251Ssam				   "%u", len);
239189251Ssam			return -1;
240189251Ssam		}
241189251Ssam		attr->e_snonce1 = pos;
242189251Ssam		break;
243189251Ssam	case ATTR_E_SNONCE2:
244189251Ssam		if (len != WPS_SECRET_NONCE_LEN) {
245189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
246189251Ssam				   "%u", len);
247189251Ssam			return -1;
248189251Ssam		}
249189251Ssam		attr->e_snonce2 = pos;
250189251Ssam		break;
251189251Ssam	case ATTR_KEY_WRAP_AUTH:
252189251Ssam		if (len != WPS_KWA_LEN) {
253189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
254189251Ssam				   "Authenticator length %u", len);
255189251Ssam			return -1;
256189251Ssam		}
257189251Ssam		attr->key_wrap_auth = pos;
258189251Ssam		break;
259189251Ssam	case ATTR_AUTH_TYPE:
260189251Ssam		if (len != 2) {
261189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
262189251Ssam				   "Type length %u", len);
263189251Ssam			return -1;
264189251Ssam		}
265189251Ssam		attr->auth_type = pos;
266189251Ssam		break;
267189251Ssam	case ATTR_ENCR_TYPE:
268189251Ssam		if (len != 2) {
269189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
270189251Ssam				   "Type length %u", len);
271189251Ssam			return -1;
272189251Ssam		}
273189251Ssam		attr->encr_type = pos;
274189251Ssam		break;
275189251Ssam	case ATTR_NETWORK_INDEX:
276189251Ssam		if (len != 1) {
277189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
278189251Ssam				   "length %u", len);
279189251Ssam			return -1;
280189251Ssam		}
281189251Ssam		attr->network_idx = pos;
282189251Ssam		break;
283189251Ssam	case ATTR_NETWORK_KEY_INDEX:
284189251Ssam		if (len != 1) {
285189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
286189251Ssam				   "length %u", len);
287189251Ssam			return -1;
288189251Ssam		}
289189251Ssam		attr->network_key_idx = pos;
290189251Ssam		break;
291189251Ssam	case ATTR_MAC_ADDR:
292189251Ssam		if (len != ETH_ALEN) {
293189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
294189251Ssam				   "length %u", len);
295189251Ssam			return -1;
296189251Ssam		}
297189251Ssam		attr->mac_addr = pos;
298189251Ssam		break;
299189251Ssam	case ATTR_KEY_PROVIDED_AUTO:
300189251Ssam		if (len != 1) {
301189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
302189251Ssam				   "Automatically length %u", len);
303189251Ssam			return -1;
304189251Ssam		}
305189251Ssam		attr->key_prov_auto = pos;
306189251Ssam		break;
307189251Ssam	case ATTR_802_1X_ENABLED:
308189251Ssam		if (len != 1) {
309189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
310189251Ssam				   "length %u", len);
311189251Ssam			return -1;
312189251Ssam		}
313189251Ssam		attr->dot1x_enabled = pos;
314189251Ssam		break;
315189251Ssam	case ATTR_SELECTED_REGISTRAR:
316189251Ssam		if (len != 1) {
317189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
318189251Ssam				   " length %u", len);
319189251Ssam			return -1;
320189251Ssam		}
321189251Ssam		attr->selected_registrar = pos;
322189251Ssam		break;
323189251Ssam	case ATTR_REQUEST_TYPE:
324189251Ssam		if (len != 1) {
325189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
326189251Ssam				   "length %u", len);
327189251Ssam			return -1;
328189251Ssam		}
329189251Ssam		attr->request_type = pos;
330189251Ssam		break;
331189251Ssam	case ATTR_RESPONSE_TYPE:
332189251Ssam		if (len != 1) {
333189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
334189251Ssam				   "length %u", len);
335189251Ssam			return -1;
336189251Ssam		}
337214734Srpaulo		attr->response_type = pos;
338189251Ssam		break;
339189251Ssam	case ATTR_MANUFACTURER:
340189251Ssam		attr->manufacturer = pos;
341189251Ssam		attr->manufacturer_len = len;
342189251Ssam		break;
343189251Ssam	case ATTR_MODEL_NAME:
344189251Ssam		attr->model_name = pos;
345189251Ssam		attr->model_name_len = len;
346189251Ssam		break;
347189251Ssam	case ATTR_MODEL_NUMBER:
348189251Ssam		attr->model_number = pos;
349189251Ssam		attr->model_number_len = len;
350189251Ssam		break;
351189251Ssam	case ATTR_SERIAL_NUMBER:
352189251Ssam		attr->serial_number = pos;
353189251Ssam		attr->serial_number_len = len;
354189251Ssam		break;
355189251Ssam	case ATTR_DEV_NAME:
356189251Ssam		attr->dev_name = pos;
357189251Ssam		attr->dev_name_len = len;
358189251Ssam		break;
359189251Ssam	case ATTR_PUBLIC_KEY:
360189251Ssam		attr->public_key = pos;
361189251Ssam		attr->public_key_len = len;
362189251Ssam		break;
363189251Ssam	case ATTR_ENCR_SETTINGS:
364189251Ssam		attr->encr_settings = pos;
365189251Ssam		attr->encr_settings_len = len;
366189251Ssam		break;
367189251Ssam	case ATTR_CRED:
368189251Ssam		if (attr->num_cred >= MAX_CRED_COUNT) {
369189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
370189251Ssam				   "attribute (max %d credentials)",
371189251Ssam				   MAX_CRED_COUNT);
372189251Ssam			break;
373189251Ssam		}
374189251Ssam		attr->cred[attr->num_cred] = pos;
375189251Ssam		attr->cred_len[attr->num_cred] = len;
376189251Ssam		attr->num_cred++;
377189251Ssam		break;
378189251Ssam	case ATTR_SSID:
379189251Ssam		attr->ssid = pos;
380189251Ssam		attr->ssid_len = len;
381189251Ssam		break;
382189251Ssam	case ATTR_NETWORK_KEY:
383189251Ssam		attr->network_key = pos;
384189251Ssam		attr->network_key_len = len;
385189251Ssam		break;
386189251Ssam	case ATTR_EAP_TYPE:
387189251Ssam		attr->eap_type = pos;
388189251Ssam		attr->eap_type_len = len;
389189251Ssam		break;
390189251Ssam	case ATTR_EAP_IDENTITY:
391189251Ssam		attr->eap_identity = pos;
392189251Ssam		attr->eap_identity_len = len;
393189251Ssam		break;
394209158Srpaulo	case ATTR_AP_SETUP_LOCKED:
395209158Srpaulo		if (len != 1) {
396209158Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
397209158Srpaulo				   "length %u", len);
398209158Srpaulo			return -1;
399209158Srpaulo		}
400209158Srpaulo		attr->ap_setup_locked = pos;
401209158Srpaulo		break;
402189251Ssam	default:
403189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
404189251Ssam			   "len=%u", type, len);
405189251Ssam		break;
406189251Ssam	}
407189251Ssam
408189251Ssam	return 0;
409189251Ssam}
410189251Ssam
411189251Ssam
412189251Ssamint wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
413189251Ssam{
414189251Ssam	const u8 *pos, *end;
415189251Ssam	u16 type, len;
416189251Ssam
417189251Ssam	os_memset(attr, 0, sizeof(*attr));
418189251Ssam	pos = wpabuf_head(msg);
419189251Ssam	end = pos + wpabuf_len(msg);
420189251Ssam
421189251Ssam	while (pos < end) {
422189251Ssam		if (end - pos < 4) {
423189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
424189251Ssam				   "%lu bytes remaining",
425189251Ssam				   (unsigned long) (end - pos));
426189251Ssam			return -1;
427189251Ssam		}
428189251Ssam
429189251Ssam		type = WPA_GET_BE16(pos);
430189251Ssam		pos += 2;
431189251Ssam		len = WPA_GET_BE16(pos);
432189251Ssam		pos += 2;
433189251Ssam		wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
434189251Ssam			   type, len);
435189251Ssam		if (len > end - pos) {
436189251Ssam			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
437189251Ssam			return -1;
438189251Ssam		}
439189251Ssam
440214734Srpaulo#ifdef WPS_WORKAROUNDS
441214734Srpaulo		if (type == 0 && len == 0) {
442214734Srpaulo			/*
443214734Srpaulo			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
444214734Srpaulo			 * end of M1. Skip those to avoid interop issues.
445214734Srpaulo			 */
446214734Srpaulo			int i;
447214734Srpaulo			for (i = 0; i < end - pos; i++) {
448214734Srpaulo				if (pos[i])
449214734Srpaulo					break;
450214734Srpaulo			}
451214734Srpaulo			if (i == end - pos) {
452214734Srpaulo				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
453214734Srpaulo					   "unexpected message padding");
454214734Srpaulo				break;
455214734Srpaulo			}
456214734Srpaulo		}
457214734Srpaulo#endif /* WPS_WORKAROUNDS */
458214734Srpaulo
459189251Ssam		if (wps_set_attr(attr, type, pos, len) < 0)
460189251Ssam			return -1;
461189251Ssam
462189251Ssam		pos += len;
463189251Ssam	}
464189251Ssam
465189251Ssam	return 0;
466189251Ssam}
467