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