]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/wps/wps_attr_parse.c
hostapd: Support Multi-AP backhaul STA onboarding with WPS
[thirdparty/hostap.git] / src / wps / wps_attr_parse.c
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
20 static 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
91 static 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
111 static 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
152 static 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
596 int 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 }