1
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
34
38
46
54
62
70
81
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
120
121
122
126
127
128
129
130
131
132
137
138
144
145
146
147
148
149
150
151
152
153
154
155
156
164
172
180
188
196
204
212
220
228
236
244
252
260
268
276
284
285
286
287
288
289
290
291
296
297
298
299
307
315
323
331
339
347
355
363
371
379
387
395
403
411
419
427
435
443
451
459
466
473
480
487
497
513
517
528
537
541
549
564
574
578
586
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
641
642
643
644
645
646
663
664
665
666
667
668
669
670
671
672
673
674
/* ... */
#include "includes.h"
#include "common.h"
#include "wps_defs.h"
#include "wps_attr_parse.h"
#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
#endif
static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
u8 id, u8 len, const u8 *pos)
{
wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
id, len);
switch (id) {
case WFA_ELEM_VERSION2:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
"%u", len);
return -1;
}{...}
attr->version2 = pos;
break;...
case WFA_ELEM_AUTHORIZEDMACS:
attr->authorized_macs = pos;
attr->authorized_macs_len = len;
break;...
case WFA_ELEM_NETWORK_KEY_SHAREABLE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
"Shareable length %u", len);
return -1;
}{...}
attr->network_key_shareable = pos;
break;...
case WFA_ELEM_REQUEST_TO_ENROLL:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
"length %u", len);
return -1;
}{...}
attr->request_to_enroll = pos;
break;...
case WFA_ELEM_SETTINGS_DELAY_TIME:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
"Time length %u", len);
return -1;
}{...}
attr->settings_delay_time = pos;
break;...
case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
len);
return -1;
}{...}
attr->registrar_configuration_methods = pos;
break;...
case WFA_ELEM_MULTI_AP:
if (len != 1) {
wpa_printf(MSG_DEBUG,
"WPS: Invalid Multi-AP Extension length %u",
len);
return -1;
}{...}
attr->multi_ap_ext = *pos;
wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
attr->multi_ap_ext);
break;...
default:
wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
"Extension subelement %u", id);
break;...
}{...}
return 0;
}{ ... }
static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
u16 len)
{
const u8 *end = pos + len;
u8 id, elen;
while (end - pos >= 2) {
id = *pos++;
elen = *pos++;
if (elen > end - pos)
break;
if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
return -1;
pos += elen;
}{...}
return 0;
}{ ... }
static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
u16 len)
{
u32 vendor_id;
if (len < 3) {
wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
return 0;
}{...}
vendor_id = WPA_GET_BE24(pos);
switch (vendor_id) {
case WPS_VENDOR_ID_WFA:
return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);...
}{...}
wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
vendor_id);
if (len > WPS_MAX_VENDOR_EXT_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
len);
return -1;
}{...}
if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
"attribute (max %d vendor extensions)",
MAX_WPS_PARSE_VENDOR_EXT);
return -1;
}{...}
attr->vendor_ext[attr->num_vendor_ext] = pos;
attr->vendor_ext_len[attr->num_vendor_ext] = len;
attr->num_vendor_ext++;
return 0;
}{ ... }
static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
const u8 *pos, u16 len)
{
switch (type) {
case ATTR_VERSION:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
len);
return -1;
}{...}
attr->version = pos;
break;...
case ATTR_MSG_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
"length %u", len);
return -1;
}{...}
attr->msg_type = pos;
break;...
case ATTR_ENROLLEE_NONCE:
if (len != WPS_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
"length %u", len);
return -1;
}{...}
attr->enrollee_nonce = pos;
break;...
case ATTR_REGISTRAR_NONCE:
if (len != WPS_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
"length %u", len);
return -1;
}{...}
attr->registrar_nonce = pos;
break;...
case ATTR_UUID_E:
if (len != WPS_UUID_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
len);
return -1;
}{...}
attr->uuid_e = pos;
break;...
case ATTR_UUID_R:
if (len != WPS_UUID_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
len);
return -1;
}{...}
attr->uuid_r = pos;
break;...
case ATTR_AUTH_TYPE_FLAGS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
"Type Flags length %u", len);
return -1;
}{...}
attr->auth_type_flags = pos;
break;...
case ATTR_ENCR_TYPE_FLAGS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
"Flags length %u", len);
return -1;
}{...}
attr->encr_type_flags = pos;
break;...
case ATTR_CONN_TYPE_FLAGS:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
"Flags length %u", len);
return -1;
}{...}
attr->conn_type_flags = pos;
break;...
case ATTR_CONFIG_METHODS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
"length %u", len);
return -1;
}{...}
attr->config_methods = pos;
break;...
case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
"Registrar Config Methods length %u", len);
return -1;
}{...}
attr->sel_reg_config_methods = pos;
break;...
case ATTR_PRIMARY_DEV_TYPE:
if (len != WPS_DEV_TYPE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
"Type length %u", len);
return -1;
}{...}
attr->primary_dev_type = pos;
break;...
case ATTR_RF_BANDS:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
"%u", len);
return -1;
}{...}
attr->rf_bands = pos;
break;...
case ATTR_ASSOC_STATE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
"length %u", len);
return -1;
}{...}
attr->assoc_state = pos;
break;...
case ATTR_CONFIG_ERROR:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
"Error length %u", len);
return -1;
}{...}
attr->config_error = pos;
break;...
case ATTR_DEV_PASSWORD_ID:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
"ID length %u", len);
return -1;
}{...}
attr->dev_password_id = pos;
break;...
case ATTR_OOB_DEVICE_PASSWORD:
if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
WPS_OOB_DEVICE_PASSWORD_LEN ||
(len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
DEV_PW_NFC_CONNECTION_HANDOVER)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
"Password length %u", len);
return -1;
}{...}
attr->oob_dev_password = pos;
attr->oob_dev_password_len = len;
break;...
case ATTR_OS_VERSION:
if (len != 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
"%u", len);
return -1;
}{...}
attr->os_version = pos;
break;...
case ATTR_WPS_STATE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
"Setup State length %u", len);
return -1;
}{...}
attr->wps_state = pos;
break;...
case ATTR_AUTHENTICATOR:
if (len != WPS_AUTHENTICATOR_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
"length %u", len);
return -1;
}{...}
attr->authenticator = pos;
break;...
case ATTR_R_HASH1:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
len);
return -1;
}{...}
attr->r_hash1 = pos;
break;...
case ATTR_R_HASH2:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
len);
return -1;
}{...}
attr->r_hash2 = pos;
break;...
case ATTR_E_HASH1:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
len);
return -1;
}{...}
attr->e_hash1 = pos;
break;...
case ATTR_E_HASH2:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
len);
return -1;
}{...}
attr->e_hash2 = pos;
break;...
case ATTR_R_SNONCE1:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
"%u", len);
return -1;
}{...}
attr->r_snonce1 = pos;
break;...
case ATTR_R_SNONCE2:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
"%u", len);
return -1;
}{...}
attr->r_snonce2 = pos;
break;...
case ATTR_E_SNONCE1:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
"%u", len);
return -1;
}{...}
attr->e_snonce1 = pos;
break;...
case ATTR_E_SNONCE2:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
"%u", len);
return -1;
}{...}
attr->e_snonce2 = pos;
break;...
case ATTR_KEY_WRAP_AUTH:
if (len != WPS_KWA_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
"Authenticator length %u", len);
return -1;
}{...}
attr->key_wrap_auth = pos;
break;...
case ATTR_AUTH_TYPE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
"Type length %u", len);
return -1;
}{...}
attr->auth_type = pos;
break;...
case ATTR_ENCR_TYPE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
"Type length %u", len);
return -1;
}{...}
attr->encr_type = pos;
break;...
case ATTR_NETWORK_INDEX:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
"length %u", len);
return -1;
}{...}
attr->network_idx = pos;
break;...
case ATTR_NETWORK_KEY_INDEX:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
"length %u", len);
return -1;
}{...}
attr->network_key_idx = pos;
break;...
case ATTR_MAC_ADDR:
if (len != ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
"length %u", len);
return -1;
}{...}
attr->mac_addr = pos;
break;...
case ATTR_SELECTED_REGISTRAR:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
" length %u", len);
return -1;
}{...}
attr->selected_registrar = pos;
break;...
case ATTR_REQUEST_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
"length %u", len);
return -1;
}{...}
attr->request_type = pos;
break;...
case ATTR_RESPONSE_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
"length %u", len);
return -1;
}{...}
attr->response_type = pos;
break;...
case ATTR_MANUFACTURER:
attr->manufacturer = pos;
if (len > WPS_MANUFACTURER_MAX_LEN)
attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
else
attr->manufacturer_len = len;
break;...
case ATTR_MODEL_NAME:
attr->model_name = pos;
if (len > WPS_MODEL_NAME_MAX_LEN)
attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
else
attr->model_name_len = len;
break;...
case ATTR_MODEL_NUMBER:
attr->model_number = pos;
if (len > WPS_MODEL_NUMBER_MAX_LEN)
attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
else
attr->model_number_len = len;
break;...
case ATTR_SERIAL_NUMBER:
attr->serial_number = pos;
if (len > WPS_SERIAL_NUMBER_MAX_LEN)
attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
else
attr->serial_number_len = len;
break;...
case ATTR_DEV_NAME:
if (len > WPS_DEV_NAME_MAX_LEN) {
wpa_printf(MSG_DEBUG,
"WPS: Ignore too long Device Name (len=%u)",
len);
break;
}{...}
attr->dev_name = pos;
attr->dev_name_len = len;
break;...
case ATTR_PUBLIC_KEY:
/* ... */
if (len < 190 || len > 192) {
wpa_printf(MSG_DEBUG,
"WPS: Ignore Public Key with unexpected length %u",
len);
break;
}{...}
attr->public_key = pos;
attr->public_key_len = len;
break;...
case ATTR_ENCR_SETTINGS:
attr->encr_settings = pos;
attr->encr_settings_len = len;
break;...
case ATTR_CRED:
if (attr->num_cred >= MAX_CRED_COUNT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
"attribute (max %d credentials)",
MAX_CRED_COUNT);
break;
}{...}
attr->cred[attr->num_cred] = pos;
attr->cred_len[attr->num_cred] = len;
attr->num_cred++;
break;...
case ATTR_SSID:
if (len > SSID_MAX_LEN) {
wpa_printf(MSG_DEBUG,
"WPS: Ignore too long SSID (len=%u)", len);
break;
}{...}
attr->ssid = pos;
attr->ssid_len = len;
break;...
case ATTR_NETWORK_KEY:
attr->network_key = pos;
attr->network_key_len = len;
break;...
case ATTR_AP_SETUP_LOCKED:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
"length %u", len);
return -1;
}{...}
attr->ap_setup_locked = pos;
break;...
case ATTR_REQUESTED_DEV_TYPE:
if (len != WPS_DEV_TYPE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
"Type length %u", len);
return -1;
}{...}
if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
"Type attribute (max %u types)",
MAX_REQ_DEV_TYPE_COUNT);
break;
}{...}
attr->req_dev_type[attr->num_req_dev_type] = pos;
attr->num_req_dev_type++;
break;...
case ATTR_SECONDARY_DEV_TYPE_LIST:
if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
(len % WPS_DEV_TYPE_LEN) > 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
"Type length %u", len);
return -1;
}{...}
attr->sec_dev_type_list = pos;
attr->sec_dev_type_list_len = len;
break;...
case ATTR_VENDOR_EXT:
if (wps_parse_vendor_ext(attr, pos, len) < 0)
return -1;
break;...
case ATTR_AP_CHANNEL:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
"length %u", len);
return -1;
}{...}
attr->ap_channel = pos;
break;...
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
"len=%u", type, len);
break;...
}{...}
return 0;
}{ ... }
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
{
const u8 *pos, *end;
u16 type, len;
#ifdef WPS_WORKAROUNDS
u16 prev_type = 0;
#endif
os_memset(attr, 0, sizeof(*attr));
pos = wpabuf_head(msg);
end = pos + wpabuf_len(msg);
while (pos < end) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
"%lu bytes remaining",
(unsigned long) (end - pos));
return -1;
}{...}
type = WPA_GET_BE16(pos);
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
type, len);
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
#ifdef WPS_WORKAROUNDS
/* ... */
if ((type & 0xff00) != 0x1000 &&
prev_type == ATTR_NETWORK_KEY) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
"to skip unexpected octet after "
"Network Key");
pos -= 3;
continue;
}{...}
#endif/* ... */
return -1;
}{...}
#ifdef WPS_WORKAROUNDS
if (type == 0 && len == 0) {
/* ... */
int i;
for (i = 0; i < end - pos; i++) {
if (pos[i])
break;
}{...}
if (i == end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
"unexpected message padding");
break;
}{...}
}{...}
#endif/* ... */
if (wps_set_attr(attr, type, pos, len) < 0)
return -1;
#ifdef WPS_WORKAROUNDS
prev_type = type;
#endif
pos += len;
}{...}
return 0;
}{ ... }