diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2013-07-04 21:12:58 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2013-07-04 21:12:58 +0000 |
commit | 083dd1de651813c2c5b040ffe3cba771aa583df1 (patch) | |
tree | fabd7f6454b47c2dac03bf9badf207872ea2410a /contrib/wpa/src/wps | |
parent | 323bbb2da1c96dac40be6115c94507c2eed99186 (diff) | |
parent | 5e9e13ee49049544adc4f40a42b737418896a338 (diff) | |
download | FreeBSD-src-083dd1de651813c2c5b040ffe3cba771aa583df1.zip FreeBSD-src-083dd1de651813c2c5b040ffe3cba771aa583df1.tar.gz |
Merge hostapd / wpa_supplicant 2.0.
Reviewed by: adrian (driver_bsd + usr.sbin/wpa)
Diffstat (limited to 'contrib/wpa/src/wps')
36 files changed, 5407 insertions, 1667 deletions
diff --git a/contrib/wpa/src/wps/http_client.c b/contrib/wpa/src/wps/http_client.c index fea2a04..c6d6c7f 100644 --- a/contrib/wpa/src/wps/http_client.c +++ b/contrib/wpa/src/wps/http_client.c @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,7 +15,7 @@ #include "http_client.h" -#define HTTP_CLIENT_TIMEOUT 30 +#define HTTP_CLIENT_TIMEOUT_SEC 30 struct http_client { @@ -42,7 +36,7 @@ struct http_client { static void http_client_timeout(void *eloop_data, void *user_ctx) { struct http_client *c = eloop_data; - wpa_printf(MSG_DEBUG, "HTTP: Timeout"); + wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c); c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); } @@ -52,6 +46,9 @@ static void http_client_got_response(struct httpread *handle, void *cookie, { struct http_client *c = cookie; + wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " + "e=%d", handle, cookie, e); + eloop_cancel_timeout(http_client_timeout, c, NULL); switch (e) { case HTTPREAD_EVENT_FILE_READY: @@ -122,7 +119,7 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) c->req = NULL; c->hread = httpread_create(c->sd, http_client_got_response, c, - c->max_response, HTTP_CLIENT_TIMEOUT); + c->max_response, HTTP_CLIENT_TIMEOUT_SEC); if (c->hread == NULL) { c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); return; @@ -181,8 +178,8 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, return NULL; } - if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout, - c, NULL)) { + if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) { http_client_free(c); return NULL; } diff --git a/contrib/wpa/src/wps/http_client.h b/contrib/wpa/src/wps/http_client.h index 924d6ab..ddee2ad 100644 --- a/contrib/wpa/src/wps/http_client.h +++ b/contrib/wpa/src/wps/http_client.h @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_CLIENT_H diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c index 356f599..6ca3214 100644 --- a/contrib/wpa/src/wps/http_server.c +++ b/contrib/wpa/src/wps/http_server.c @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/wpa/src/wps/http_server.h b/contrib/wpa/src/wps/http_server.h index 219941c..4b2b749 100644 --- a/contrib/wpa/src/wps/http_server.h +++ b/contrib/wpa/src/wps/http_server.h @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_SERVER_H diff --git a/contrib/wpa/src/wps/httpread.c b/contrib/wpa/src/wps/httpread.c index 40422e4..ad4f4a1 100644 --- a/contrib/wpa/src/wps/httpread.c +++ b/contrib/wpa/src/wps/httpread.c @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * The files are buffered via internal callbacks from eloop, then presented to * an application callback routine when completely read into memory. May also diff --git a/contrib/wpa/src/wps/httpread.h b/contrib/wpa/src/wps/httpread.h index 51aa214..454b618 100644 --- a/contrib/wpa/src/wps/httpread.h +++ b/contrib/wpa/src/wps/httpread.h @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTPREAD_H diff --git a/contrib/wpa/src/wps/ndef.c b/contrib/wpa/src/wps/ndef.c index 9baec7f..a48a2d7 100644 --- a/contrib/wpa/src/wps/ndef.c +++ b/contrib/wpa/src/wps/ndef.c @@ -1,34 +1,28 @@ /* * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "wps/wps.h" -#include "wps/wps_i.h" #define FLAG_MESSAGE_BEGIN (1 << 7) #define FLAG_MESSAGE_END (1 << 6) #define FLAG_CHUNK (1 << 5) #define FLAG_SHORT_RECORD (1 << 4) #define FLAG_ID_LENGTH_PRESENT (1 << 3) +#define FLAG_TNF_NFC_FORUM (0x01) #define FLAG_TNF_RFC2046 (0x02) struct ndef_record { - u8 *type; - u8 *id; - u8 *payload; + const u8 *type; + const u8 *id; + const u8 *payload; u8 type_length; u8 id_length; u32 payload_length; @@ -37,9 +31,10 @@ struct ndef_record { static char wifi_handover_type[] = "application/vnd.wfa.wsc"; -static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) +static int ndef_parse_record(const u8 *data, u32 size, + struct ndef_record *record) { - u8 *pos = data + 1; + const u8 *pos = data + 1; if (size < 2) return -1; @@ -78,12 +73,12 @@ static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) } -static struct wpabuf * ndef_parse_records(struct wpabuf *buf, +static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, int (*filter)(struct ndef_record *)) { struct ndef_record record; int len = wpabuf_len(buf); - u8 *data = wpabuf_mhead(buf); + const u8 *data = wpabuf_head(buf); while (len > 0) { if (ndef_parse_record(data, len, &record) < 0) { @@ -103,13 +98,14 @@ static struct wpabuf * ndef_parse_records(struct wpabuf *buf, static struct wpabuf * ndef_build_record(u8 flags, void *type, u8 type_length, void *id, - u8 id_length, void *payload, - u32 payload_length) + u8 id_length, + const struct wpabuf *payload) { struct wpabuf *record; size_t total_len; int short_record; u8 local_flag; + size_t payload_length = wpabuf_len(payload); short_record = payload_length < 256 ? 1 : 0; @@ -144,7 +140,7 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type, wpabuf_put_u8(record, id_length); wpabuf_put_data(record, type, type_length); wpabuf_put_data(record, id, id_length); - wpabuf_put_data(record, payload, payload_length); + wpabuf_put_buf(record, payload); return record; } @@ -160,16 +156,90 @@ static int wifi_filter(struct ndef_record *record) } -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf) +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) { return ndef_parse_records(buf, wifi_filter); } -struct wpabuf * ndef_build_wifi(struct wpabuf *buf) +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) { return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | FLAG_TNF_RFC2046, wifi_handover_type, - os_strlen(wifi_handover_type), NULL, 0, - wpabuf_mhead(buf), wpabuf_len(buf)); + os_strlen(wifi_handover_type), NULL, 0, buf); +} + + +struct wpabuf * ndef_build_wifi_hr(void) +{ + struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr; + struct wpabuf *carrier, *hc; + + rn = wpabuf_alloc(2); + if (rn == NULL) + return NULL; + wpabuf_put_be16(rn, os_random() & 0xffff); + + cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2, + NULL, 0, rn); + wpabuf_free(rn); + + if (cr == NULL) + return NULL; + + ac_payload = wpabuf_alloc(4); + if (ac_payload == NULL) { + wpabuf_free(cr); + return NULL; + } + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */ + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */ + wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */ + wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */ + + ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2, + NULL, 0, ac_payload); + wpabuf_free(ac_payload); + if (ac == NULL) { + wpabuf_free(cr); + return NULL; + } + + hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac)); + if (hr_payload == NULL) { + wpabuf_free(cr); + wpabuf_free(ac); + return NULL; + } + + wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */ + wpabuf_put_buf(hr_payload, cr); + wpabuf_put_buf(hr_payload, ac); + wpabuf_free(cr); + wpabuf_free(ac); + + hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2, + NULL, 0, hr_payload); + wpabuf_free(hr_payload); + if (hr == NULL) + return NULL; + + carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type)); + if (carrier == NULL) { + wpabuf_free(hr); + return NULL; + } + wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */ + wpabuf_put_u8(carrier, os_strlen(wifi_handover_type)); + wpabuf_put_str(carrier, wifi_handover_type); + + hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2, + "0", 1, carrier); + wpabuf_free(carrier); + if (hc == NULL) { + wpabuf_free(hr); + return NULL; + } + + return wpabuf_concat(hr, hc); } diff --git a/contrib/wpa/src/wps/upnp_xml.c b/contrib/wpa/src/wps/upnp_xml.c index b1b1e2b..a9958ee 100644 --- a/contrib/wpa/src/wps/upnp_xml.c +++ b/contrib/wpa/src/wps/upnp_xml.c @@ -75,8 +75,8 @@ * Note that angle brackets present in the original data must have been encoded * as < and > so they will not trouble us. */ -static int xml_next_tag(const char *in, const char **out, - const char **out_tagname, const char **end) +int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end) { while (*in && *in != '<') in++; diff --git a/contrib/wpa/src/wps/upnp_xml.h b/contrib/wpa/src/wps/upnp_xml.h index 62dbe60..616af3d 100644 --- a/contrib/wpa/src/wps/upnp_xml.h +++ b/contrib/wpa/src/wps/upnp_xml.h @@ -16,6 +16,8 @@ void xml_data_encode(struct wpabuf *buf, const char *data, int len); void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data); +int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end); char * xml_get_first_item(const char *doc, const char *item); struct wpabuf * xml_get_base64_item(const char *data, const char *name, enum http_reply_code *ret); diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c index 619af15..2575705 100644 --- a/contrib/wpa/src/wps/wps.c +++ b/contrib/wpa/src/wps/wps.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,6 +15,12 @@ #include "wps_dev_attr.h" +#ifdef CONFIG_WPS_TESTING +int wps_version_number = 0x20; +int wps_testing_dummy_cred = 0; +#endif /* CONFIG_WPS_TESTING */ + + /** * wps_init - Initialize WPS Registration protocol data * @cfg: WPS configuration @@ -45,8 +45,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); } if (cfg->pin) { - data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ? - DEV_PW_DEFAULT : data->wps->oob_dev_pw_id; + data->dev_pw_id = cfg->dev_pw_id; data->dev_password = os_malloc(cfg->pin_len); if (data->dev_password == NULL) { os_free(data); @@ -56,17 +55,33 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->dev_password_len = cfg->pin_len; } +#ifdef CONFIG_WPS_NFC + if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) { + data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; + os_free(data->dev_password); + data->dev_password = + os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->dev_password, + wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); + } +#endif /* CONFIG_WPS_NFC */ + data->pbc = cfg->pbc; if (cfg->pbc) { /* Use special PIN '00000000' for PBC */ data->dev_pw_id = DEV_PW_PUSHBUTTON; os_free(data->dev_password); - data->dev_password = os_malloc(8); + data->dev_password = (u8 *) os_strdup("00000000"); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memset(data->dev_password, '0', 8); data->dev_password_len = 8; } @@ -94,6 +109,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->new_ap_settings = os_malloc(sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { + os_free(data->dev_password); os_free(data); return NULL; } @@ -103,8 +119,11 @@ struct wps_data * wps_init(const struct wps_config *cfg) if (cfg->peer_addr) os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN); + if (cfg->p2p_dev_addr) + os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN); data->use_psk_key = cfg->use_psk_key; + data->pbc_in_m1 = cfg->pbc_in_m1; return data; } @@ -116,6 +135,12 @@ struct wps_data * wps_init(const struct wps_config *cfg) */ void wps_deinit(struct wps_data *data) { +#ifdef CONFIG_WPS_NFC + if (data->registrar && data->nfc_pw_token) + wps_registrar_remove_nfc_pw_token(data->wps->registrar, + data->nfc_pw_token); +#endif /* CONFIG_WPS_NFC */ + if (data->wps_pin_revealed) { wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and " "negotiation failed"); @@ -134,6 +159,7 @@ void wps_deinit(struct wps_data *data) wps_device_data_free(&data->peer_dev); os_free(data->new_ap_settings); dh5_free(data->dh_ctx); + os_free(data->nfc_pw_token); os_free(data); } @@ -201,19 +227,19 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg) WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) return 0; +#ifdef CONFIG_WPS_STRICT + if (!attr.sel_reg_config_methods || + !(WPA_GET_BE16(attr.sel_reg_config_methods) & + WPS_CONFIG_PUSHBUTTON)) + return 0; +#endif /* CONFIG_WPS_STRICT */ + return 1; } -/** - * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN - * @msg: WPS IE contents from Beacon or Probe Response frame - * Returns: 1 if PIN Registrar is active, 0 if not - */ -int wps_is_selected_pin_registrar(const struct wpabuf *msg) +static int is_selected_pin_registrar(struct wps_parse_attr *attr) { - struct wps_parse_attr attr; - /* * In theory, this could also verify that attr.sel_reg_config_methods * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, @@ -222,21 +248,114 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg) * Device Password ID here. */ - if (wps_parse_msg(msg, &attr) < 0) + if (!attr->selected_registrar || *attr->selected_registrar == 0) return 0; - if (!attr.selected_registrar || *attr.selected_registrar == 0) + if (attr->dev_password_id != NULL && + WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) return 0; - if (attr.dev_password_id != NULL && - WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON) +#ifdef CONFIG_WPS_STRICT + if (!attr->sel_reg_config_methods || + !(WPA_GET_BE16(attr->sel_reg_config_methods) & + (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD))) return 0; +#endif /* CONFIG_WPS_STRICT */ return 1; } /** + * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN + * @msg: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if PIN Registrar is active, 0 if not + */ +int wps_is_selected_pin_registrar(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + return is_selected_pin_registrar(&attr); +} + + +/** + * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address + * @msg: WPS IE contents from Beacon or Probe Response frame + * @addr: MAC address to search for + * @ver1_compat: Whether to use version 1 compatibility mode + * Returns: 2 if the specified address is explicit authorized, 1 if address is + * authorized (broadcast), 0 if not + */ +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat) +{ + struct wps_parse_attr attr; + unsigned int i; + const u8 *pos; + const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + if (!attr.version2 && ver1_compat) { + /* + * Version 1.0 AP - AuthorizedMACs not used, so revert back to + * old mechanism of using SelectedRegistrar. + */ + return is_selected_pin_registrar(&attr); + } + + if (!attr.authorized_macs) + return 0; + + pos = attr.authorized_macs; + for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { + if (os_memcmp(pos, addr, ETH_ALEN) == 0) + return 2; + if (os_memcmp(pos, bcast, ETH_ALEN) == 0) + return 1; + pos += ETH_ALEN; + } + + return 0; +} + + +/** + * wps_ap_priority_compar - Prioritize WPS IE from two APs + * @wps_a: WPS IE contents from Beacon or Probe Response frame + * @wps_b: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if wps_b is considered more likely selection for WPS + * provisioning, -1 if wps_a is considered more like, or 0 if no preference + */ +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b) +{ + struct wps_parse_attr attr_a, attr_b; + int sel_a, sel_b; + + if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + return 1; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) + return -1; + + sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; + sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + + if (sel_a && !sel_b) + return -1; + if (!sel_a && sel_b) + return 1; + + return 0; +} + + +/** * wps_get_uuid_e - Get UUID-E from WPS IE * @msg: WPS IE contents from Beacon or Probe Response frame * Returns: Pointer to UUID-E or %NULL if not included @@ -255,6 +374,19 @@ const u8 * wps_get_uuid_e(const struct wpabuf *msg) /** + * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0 + */ +int wps_is_20(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (msg == NULL || wps_parse_msg(msg, &attr) < 0) + return 0; + return attr.version2 != NULL; +} + + +/** * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request * @req_type: Value for Request Type attribute * Returns: WPS IE or %NULL on failure @@ -277,7 +409,8 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); if (wps_build_version(ie) || - wps_build_req_type(ie, req_type)) { + wps_build_req_type(ie, req_type) || + wps_build_wfa_ext(ie, 0, NULL, 0)) { wpabuf_free(ie); return NULL; } @@ -310,7 +443,8 @@ struct wpabuf * wps_build_assoc_resp_ie(void) wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); if (wps_build_version(ie) || - wps_build_resp_type(ie, WPS_RESP_AP)) { + wps_build_resp_type(ie, WPS_RESP_AP) || + wps_build_wfa_ext(ie, 0, NULL, 0)) { wpabuf_free(ie); return NULL; } @@ -323,62 +457,64 @@ struct wpabuf * wps_build_assoc_resp_ie(void) /** * wps_build_probe_req_ie - Build WPS IE for Probe Request - * @pbc: Whether searching for PBC mode APs + * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for + * most other use cases) * @dev: Device attributes * @uuid: Own UUID * @req_type: Value for Request Type attribute + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or + * %NULL if none * Returns: WPS IE or %NULL on failure * * The caller is responsible for freeing the buffer. */ -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, - enum wps_request_type req_type) + enum wps_request_type req_type, + unsigned int num_req_dev_types, + const u8 *req_dev_types) { struct wpabuf *ie; - u8 *len; - u16 methods; wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request"); - ie = wpabuf_alloc(200); + ie = wpabuf_alloc(500); if (ie == NULL) return NULL; - wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); - len = wpabuf_put(ie, 1); - wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); - - if (pbc) - methods = WPS_CONFIG_PUSHBUTTON; - else { - methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | - WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS_UFD - methods |= WPS_CONFIG_USBA; -#endif /* CONFIG_WPS_UFD */ -#ifdef CONFIG_WPS_NFC - methods |= WPS_CONFIG_NFC_INTERFACE; -#endif /* CONFIG_WPS_NFC */ - } - if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || - wps_build_config_methods(ie, methods) || + wps_build_config_methods(ie, dev->config_methods) || wps_build_uuid_e(ie, uuid) || wps_build_primary_dev_type(dev, ie) || wps_build_rf_bands(dev, ie) || wps_build_assoc_state(NULL, ie) || wps_build_config_error(ie, WPS_CFG_NO_ERROR) || - wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON : - DEV_PW_DEFAULT)) { + wps_build_dev_password_id(ie, pw_id) || +#ifdef CONFIG_WPS2 + wps_build_manufacturer(dev, ie) || + wps_build_model_name(dev, ie) || + wps_build_model_number(dev, ie) || + wps_build_dev_name(dev, ie) || + wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || +#endif /* CONFIG_WPS2 */ + wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) + || + wps_build_secondary_dev_type(dev, ie) + ) { wpabuf_free(ie); return NULL; } - *len = wpabuf_len(ie) - 2; +#ifndef CONFIG_WPS2 + if (dev->p2p && wps_build_dev_name(dev, ie)) { + wpabuf_free(ie); + return NULL; + } +#endif /* CONFIG_WPS2 */ - return ie; + return wps_ie_encapsulate(ie); } diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index 1fd1e52..c6b7099 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_H @@ -33,6 +27,7 @@ enum wsc_op_code { struct wps_registrar; struct upnp_wps_device_sm; struct wps_er; +struct wps_parse_attr; /** * struct wps_credential - WPS Credential @@ -47,6 +42,7 @@ struct wps_er; * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets + * @ap_channel: AP channel */ struct wps_credential { u8 ssid[32]; @@ -59,10 +55,18 @@ struct wps_credential { u8 mac_addr[ETH_ALEN]; const u8 *cred_attr; size_t cred_attr_len; + u16 ap_channel; }; #define WPS_DEV_TYPE_LEN 8 #define WPS_DEV_TYPE_BUFSIZE 21 +#define WPS_SEC_DEV_TYPE_MAX_LEN 128 +/* maximum number of advertised WPS vendor extension attributes */ +#define MAX_WPS_VENDOR_EXTENSIONS 10 +/* maximum size of WPS Vendor extension attribute */ +#define WPS_MAX_VENDOR_EXT_LEN 1024 +/* maximum number of parsed WPS vendor extension attributes */ +#define MAX_WPS_PARSE_VENDOR_EXT 10 /** * struct wps_device_data - WPS Device Data @@ -73,8 +77,11 @@ struct wps_credential { * @model_number: Model Number (0..32 octets encoded in UTF-8) * @serial_number: Serial Number (0..32 octets encoded in UTF-8) * @pri_dev_type: Primary Device Type + * @sec_dev_type: Array of secondary device types + * @num_sec_dev_type: Number of secondary device types * @os_version: OS Version * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) + * @p2p: Whether the device is a P2P device */ struct wps_device_data { u8 mac_addr[ETH_ALEN]; @@ -84,19 +91,16 @@ struct wps_device_data { char *model_number; char *serial_number; u8 pri_dev_type[WPS_DEV_TYPE_LEN]; +#define WPS_SEC_DEVICE_TYPES 5 + u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN]; + u8 num_sec_dev_types; u32 os_version; u8 rf_bands; -}; + u16 config_methods; + struct wpabuf *vendor_ext_m1; + struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; -struct oob_conf_data { - enum { - OOB_METHOD_UNKNOWN = 0, - OOB_METHOD_DEV_PWD_E, - OOB_METHOD_DEV_PWD_R, - OOB_METHOD_CRED, - } oob_method; - struct wpabuf *dev_password; - struct wpabuf *pubkey_hash; + int p2p; }; /** @@ -156,6 +160,29 @@ struct wps_config { * struct wpa_context::psk. */ int use_psk_key; + + /** + * dev_pw_id - Device Password ID for Enrollee when PIN is used + */ + u16 dev_pw_id; + + /** + * p2p_dev_addr - P2P Device Address from (Re)Association Request + * + * On AP/GO, this is set to the P2P Device Address of the associating + * P2P client if a P2P IE is included in the (Re)Association Request + * frame and the P2P Device Address is included. Otherwise, this is set + * to %NULL to indicate the station does not have a P2P Device Address. + */ + const u8 *p2p_dev_addr; + + /** + * pbc_in_m1 - Do not remove PushButton config method in M1 (AP) + * + * This can be used to enable a workaround to allow Windows 7 to use + * PBC with the AP. + */ + int pbc_in_m1; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -195,13 +222,20 @@ struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code); int wps_is_selected_pbc_registrar(const struct wpabuf *msg); int wps_is_selected_pin_registrar(const struct wpabuf *msg); +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b); +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat); const u8 * wps_get_uuid_e(const struct wpabuf *msg); +int wps_is_20(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); struct wpabuf * wps_build_assoc_resp_ie(void); -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, - enum wps_request_type req_type); + enum wps_request_type req_type, + unsigned int num_req_dev_types, + const u8 *req_dev_types); /** @@ -253,12 +287,15 @@ struct wps_registrar_config { * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee * @uuid_e: UUID-E of the Enrollee + * @dev_pw: Device Password (PIN) used during registration + * @dev_pw_len: Length of dev_pw in octets * * This callback is called whenever an Enrollee completes registration * successfully. */ void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); /** * set_sel_reg_cb - Callback for reporting selected registrar changes @@ -340,6 +377,11 @@ struct wps_registrar_config { * static_wep_only - Whether the BSS supports only static WEP */ int static_wep_only; + + /** + * dualband - Whether this is a concurrent dualband AP + */ + int dualband; }; @@ -395,7 +437,22 @@ enum wps_event { /** * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed */ - WPS_EV_ER_ENROLLEE_REMOVE + WPS_EV_ER_ENROLLEE_REMOVE, + + /** + * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned + */ + WPS_EV_ER_AP_SETTINGS, + + /** + * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event + */ + WPS_EV_ER_SET_SELECTED_REGISTRAR, + + /** + * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN + */ + WPS_EV_AP_PIN_SUCCESS }; /** @@ -428,6 +485,8 @@ union wps_event_data { */ struct wps_event_fail { int msg; + u16 config_error; + u16 error_indication; } fail; struct wps_event_pwd_auth_fail { @@ -464,6 +523,23 @@ union wps_event_data { const char *model_number; const char *serial_number; } enrollee; + + struct wps_event_er_ap_settings { + const u8 *uuid; + const struct wps_credential *cred; + } ap_settings; + + struct wps_event_er_set_selected_registrar { + const u8 *uuid; + int sel_reg; + u16 dev_passwd_id; + u16 sel_reg_config_methods; + enum { + WPS_ER_SET_SEL_REG_START, + WPS_ER_SET_SEL_REG_DONE, + WPS_ER_SET_SEL_REG_FAILED + } state; + } set_sel_reg; }; /** @@ -532,16 +608,6 @@ struct wps_context { struct wps_device_data dev; /** - * oob_conf - OOB Config data - */ - struct oob_conf_data oob_conf; - - /** - * oob_dev_pw_id - OOB Device password id - */ - u16 oob_dev_pw_id; - - /** * dh_ctx - Context data for Diffie-Hellman operation */ void *dh_ctx; @@ -672,53 +738,57 @@ struct wps_context { /* Pending messages from UPnP PutWLANResponse */ struct upnp_pending_message *upnp_msgs; -}; - -struct oob_device_data { - char *device_name; - char *device_path; - void * (*init_func)(struct wps_context *, struct oob_device_data *, - int); - struct wpabuf * (*read_func)(void *); - int (*write_func)(void *, struct wpabuf *); - void (*deinit_func)(void *); -}; -struct oob_nfc_device_data { - int (*init_func)(char *); - void * (*read_func)(size_t *); - int (*write_func)(void *, size_t); - void (*deinit_func)(void); + u16 ap_nfc_dev_pw_id; + struct wpabuf *ap_nfc_dh_pubkey; + struct wpabuf *ap_nfc_dh_privkey; + struct wpabuf *ap_nfc_dev_pw; }; struct wps_registrar * wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); -int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len, int timeout); +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, const u8 *pin, size_t pin_len, + int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); +int wps_registrar_wps_cancel(struct wps_registrar *reg); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); -int wps_registrar_button_pushed(struct wps_registrar *reg); +int wps_registrar_button_pushed(struct wps_registrar *reg, + const u8 *p2p_dev_addr); +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, - const struct wpabuf *wps_data); + const struct wpabuf *wps_data, + int p2p_wildcard); int wps_registrar_update_ie(struct wps_registrar *reg); int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen); +int wps_registrar_config_ap(struct wps_registrar *reg, + struct wps_credential *cred); +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len); +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len); + +int wps_build_credential_wrap(struct wpabuf *msg, + const struct wps_credential *cred); unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); unsigned int wps_generate_pin(void); +int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); -struct oob_device_data * wps_get_oob_device(char *device_type); -struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name); -int wps_get_oob_method(char *method); -int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, - int registrar); +struct wpabuf * wps_get_oob_cred(struct wps_context *wps); +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr); int wps_attr_text(struct wpabuf *data, char *buf, char *end); -struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname); +struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname, + const char *filter); void wps_er_refresh(struct wps_er *er); void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, @@ -726,11 +796,173 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, int wps_er_pbc(struct wps_er *er, const u8 *uuid); int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, size_t pin_len); +int wps_er_set_config(struct wps_er *er, const u8 *uuid, + const struct wps_credential *cred); +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len, const struct wps_credential *cred); +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid); int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]); char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, size_t buf_len); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); u16 wps_config_methods_str2bin(const char *str); +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw); +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw); + +/* ndef.c */ +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_build_wifi_hr(void); + +#ifdef CONFIG_WPS_STRICT +int wps_validate_beacon(const struct wpabuf *wps_ie); +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, + const u8 *addr); +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr); +int wps_validate_assoc_req(const struct wpabuf *wps_ie); +int wps_validate_assoc_resp(const struct wpabuf *wps_ie); +int wps_validate_m1(const struct wpabuf *tlvs); +int wps_validate_m2(const struct wpabuf *tlvs); +int wps_validate_m2d(const struct wpabuf *tlvs); +int wps_validate_m3(const struct wpabuf *tlvs); +int wps_validate_m4(const struct wpabuf *tlvs); +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m5(const struct wpabuf *tlvs); +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m6(const struct wpabuf *tlvs); +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2); +int wps_validate_m7(const struct wpabuf *tlvs); +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2); +int wps_validate_m8(const struct wpabuf *tlvs); +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2); +int wps_validate_wsc_ack(const struct wpabuf *tlvs); +int wps_validate_wsc_nack(const struct wpabuf *tlvs); +int wps_validate_wsc_done(const struct wpabuf *tlvs); +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs); +#else /* CONFIG_WPS_STRICT */ +static inline int wps_validate_beacon(const struct wpabuf *wps_ie){ + return 0; +} + +static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, + int probe, const u8 *addr) +{ + return 0; +} + +static inline int wps_validate_probe_req(const struct wpabuf *wps_ie, + const u8 *addr) +{ + return 0; +} + +static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie) +{ + return 0; +} + +static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie) +{ + return 0; +} + +static inline int wps_validate_m1(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m2(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m2d(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m3(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m4(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m5(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m6(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2) +{ + return 0; +} + +static inline int wps_validate_m7(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, + int wps2) +{ + return 0; +} + +static inline int wps_validate_m8(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, + int wps2) +{ + return 0; +} + +static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_wsc_done(const struct wpabuf *tlvs) +{ + return 0; +} + +static inline int wps_validate_upnp_set_selected_registrar( + const struct wpabuf *tlvs) +{ + return 0; +} +#endif /* CONFIG_WPS_STRICT */ #endif /* WPS_H */ diff --git a/contrib/wpa/src/wps/wps_attr_build.c b/contrib/wpa/src/wps/wps_attr_build.c index 9da556a..29aee8e 100644 --- a/contrib/wpa/src/wps/wps_attr_build.c +++ b/contrib/wpa/src/wps/wps_attr_build.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute building * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,8 @@ #include "crypto/crypto.h" #include "crypto/dh_group5.h" #include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" #include "wps_i.h" @@ -34,6 +30,14 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) wps->dh_ctx = wps->wps->dh_ctx; wps->wps->dh_ctx = NULL; pubkey = wpabuf_dup(wps->wps->dh_pubkey); +#ifdef CONFIG_WPS_NFC + } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap && + wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) { + wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys"); + wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); + pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); +#endif /* CONFIG_WPS_NFC */ } else { wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); wps->dh_privkey = NULL; @@ -47,6 +51,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) wpabuf_free(pubkey); return -1; } + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); + wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey); wpabuf_put_be16(msg, ATTR_PUBLIC_KEY); wpabuf_put_be16(msg, wpabuf_len(pubkey)); @@ -156,10 +162,65 @@ int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg) int wps_build_version(struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Version"); + /* + * Note: This attribute is deprecated and set to hardcoded 0x10 for + * backwards compatibility reasons. The real version negotiation is + * done with Version2. + */ + wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)"); wpabuf_put_be16(msg, ATTR_VERSION); wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 0x10); + return 0; +} + + +int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, + const u8 *auth_macs, size_t auth_macs_count) +{ +#ifdef CONFIG_WPS2 + u8 *len; + + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + len = wpabuf_put(msg, 2); /* to be filled */ + wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA); + + wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION); + wpabuf_put_u8(msg, WFA_ELEM_VERSION2); + wpabuf_put_u8(msg, 1); wpabuf_put_u8(msg, WPS_VERSION); + + if (req_to_enroll) { + wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)"); + wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL); + wpabuf_put_u8(msg, 1); + wpabuf_put_u8(msg, 1); + } + + if (auth_macs && auth_macs_count) { + size_t i; + wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)", + (int) auth_macs_count); + wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS); + wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN); + wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN); + for (i = 0; i < auth_macs_count; i++) + wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR, + MAC2STR(&auth_macs[i * ETH_ALEN])); + } + + WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_WPS_TESTING + if (WPS_VERSION > 0x20) { + wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra " + "attribute"); + wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, 42); + } +#endif /* CONFIG_WPS_TESTING */ return 0; } @@ -196,20 +257,28 @@ int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg) int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) { + u16 auth_types = WPS_AUTH_TYPES; +#ifdef CONFIG_WPS2 + auth_types &= ~WPS_AUTH_SHARED; +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, WPS_AUTH_TYPES); + wpabuf_put_be16(msg, auth_types); return 0; } int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) { + u16 encr_types = WPS_ENCR_TYPES; +#ifdef CONFIG_WPS2 + encr_types &= ~WPS_ENCR_WEP; +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, WPS_ENCR_TYPES); + wpabuf_put_be16(msg, encr_types); return 0; } @@ -266,7 +335,7 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, wpabuf_put_be16(msg, block_size + wpabuf_len(plain)); iv = wpabuf_put(msg, block_size); - if (os_get_random(iv, block_size) < 0) + if (random_get_bytes(iv, block_size) < 0) return -1; data = wpabuf_put(msg, 0); @@ -279,44 +348,56 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, #ifdef CONFIG_WPS_OOB -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; - u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; - - wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); - addr[0] = wpabuf_head(wps->dh_pubkey); - hash_len = wpabuf_len(wps->dh_pubkey); + addr[0] = wpabuf_head(pubkey); + hash_len = wpabuf_len(pubkey); sha256_vector(1, addr, &hash_len, pubkey_hash); - if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { - wpa_printf(MSG_ERROR, "WPS: device password id " - "generation error"); - return -1; - } - wps->oob_dev_pw_id |= 0x0010; - - if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) { - wpa_printf(MSG_ERROR, "WPS: OOB device password " - "generation error"); - return -1; - } - wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); - wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); - wpabuf_put_be16(msg, wps->oob_dev_pw_id); - wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); - - wpa_snprintf_hex_uppercase( - wpabuf_put(wps->oob_conf.dev_password, - wpabuf_size(wps->oob_conf.dev_password)), - wpabuf_size(wps->oob_conf.dev_password), - dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); + wpabuf_put_be16(msg, dev_pw_id); + wpabuf_put_data(msg, dev_pw, dev_pw_len); return 0; } #endif /* CONFIG_WPS_OOB */ + + +/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ +struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + ie = wpabuf_alloc(wpabuf_len(data) + 100); + if (ie == NULL) { + wpabuf_free(data); + return NULL; + } + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + wpabuf_free(data); + + return ie; +} diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c index 30b0e79..3999b1b8 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.c +++ b/contrib/wpa/src/wps/wps_attr_parse.c @@ -2,22 +2,132 @@ * Wi-Fi Protected Setup - attribute parsing * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "wps_i.h" +#include "wps_defs.h" +#include "wps_attr_parse.h" +#ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS +#endif /* CONFIG_WPS_STRICT */ + + +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; + 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 (pos + 2 < end) { + id = *pos++; + elen = *pos++; + if (pos + elen > end) + 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); + } + + /* Handle unknown vendor extensions */ + + 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, @@ -153,12 +263,16 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->dev_password_id = pos; break; case ATTR_OOB_DEVICE_PASSWORD: - if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) { + if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN || + len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) { 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) { @@ -399,6 +513,43 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, } 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); @@ -413,6 +564,9 @@ 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 /* WPS_WORKAROUNDS */ os_memset(attr, 0, sizeof(*attr)); pos = wpabuf_head(msg); @@ -430,10 +584,28 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) pos += 2; len = WPA_GET_BE16(pos); pos += 2; - wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u", + 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 + /* + * Some deployed APs seem to have a bug in encoding of + * Network Key attribute in the Credential attribute + * where they add an extra octet after the Network Key + * attribute at least when open network is being + * provisioned. + */ + 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 /* WPS_WORKAROUNDS */ return -1; } @@ -459,6 +631,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) if (wps_set_attr(attr, type, pos, len) < 0) return -1; +#ifdef WPS_WORKAROUNDS + prev_type = type; +#endif /* WPS_WORKAROUNDS */ pos += len; } diff --git a/contrib/wpa/src/wps/wps_attr_parse.h b/contrib/wpa/src/wps/wps_attr_parse.h new file mode 100644 index 0000000..88e51a4 --- /dev/null +++ b/contrib/wpa/src/wps/wps_attr_parse.h @@ -0,0 +1,108 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_ATTR_PARSE_H +#define WPS_ATTR_PARSE_H + +#include "wps.h" + +struct wps_parse_attr { + /* fixed length fields */ + const u8 *version; /* 1 octet */ + const u8 *version2; /* 1 octet */ + const u8 *msg_type; /* 1 octet */ + const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ + const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ + const u8 *auth_type_flags; /* 2 octets */ + const u8 *encr_type_flags; /* 2 octets */ + const u8 *conn_type_flags; /* 1 octet */ + const u8 *config_methods; /* 2 octets */ + const u8 *sel_reg_config_methods; /* 2 octets */ + const u8 *primary_dev_type; /* 8 octets */ + const u8 *rf_bands; /* 1 octet */ + const u8 *assoc_state; /* 2 octets */ + const u8 *config_error; /* 2 octets */ + const u8 *dev_password_id; /* 2 octets */ + const u8 *os_version; /* 4 octets */ + const u8 *wps_state; /* 1 octet */ + const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ + const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ + const u8 *auth_type; /* 2 octets */ + const u8 *encr_type; /* 2 octets */ + const u8 *network_idx; /* 1 octet */ + const u8 *network_key_idx; /* 1 octet */ + const u8 *mac_addr; /* ETH_ALEN (6) octets */ + const u8 *key_prov_auto; /* 1 octet (Bool) */ + const u8 *dot1x_enabled; /* 1 octet (Bool) */ + const u8 *selected_registrar; /* 1 octet (Bool) */ + const u8 *request_type; /* 1 octet */ + const u8 *response_type; /* 1 octet */ + const u8 *ap_setup_locked; /* 1 octet */ + const u8 *settings_delay_time; /* 1 octet */ + const u8 *network_key_shareable; /* 1 octet (Bool) */ + const u8 *request_to_enroll; /* 1 octet (Bool) */ + const u8 *ap_channel; /* 2 octets */ + + /* variable length fields */ + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + const u8 *dev_name; + size_t dev_name_len; + const u8 *public_key; + size_t public_key_len; + const u8 *encr_settings; + size_t encr_settings_len; + const u8 *ssid; /* <= 32 octets */ + size_t ssid_len; + const u8 *network_key; /* <= 64 octets */ + size_t network_key_len; + const u8 *eap_type; /* <= 8 octets */ + size_t eap_type_len; + const u8 *eap_identity; /* <= 64 octets */ + size_t eap_identity_len; + const u8 *authorized_macs; /* <= 30 octets */ + size_t authorized_macs_len; + const u8 *sec_dev_type_list; /* <= 128 octets */ + size_t sec_dev_type_list_len; + const u8 *oob_dev_password; /* 38..54 octets */ + size_t oob_dev_password_len; + + /* attributes that can occur multiple times */ +#define MAX_CRED_COUNT 10 + const u8 *cred[MAX_CRED_COUNT]; + size_t cred_len[MAX_CRED_COUNT]; + size_t num_cred; + +#define MAX_REQ_DEV_TYPE_COUNT 10 + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; + size_t num_req_dev_type; + + const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; + size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + size_t num_vendor_ext; +}; + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); + +#endif /* WPS_ATTR_PARSE_H */ diff --git a/contrib/wpa/src/wps/wps_attr_process.c b/contrib/wpa/src/wps/wps_attr_process.c index 4751bbc..b81f106 100644 --- a/contrib/wpa/src/wps/wps_attr_process.c +++ b/contrib/wpa/src/wps/wps_attr_process.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute processing * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -264,11 +258,31 @@ static int wps_process_cred_802_1x_enabled(struct wps_credential *cred, } -static void wps_workaround_cred_key(struct wps_credential *cred) +static int wps_process_cred_ap_channel(struct wps_credential *cred, + const u8 *ap_channel) +{ + if (ap_channel == NULL) + return 0; /* optional attribute */ + + cred->ap_channel = WPA_GET_BE16(ap_channel); + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel); + + return 0; +} + + +static int wps_workaround_cred_key(struct wps_credential *cred) { if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && cred->key_len > 8 && cred->key_len < 64 && cred->key[cred->key_len - 1] == 0) { +#ifdef CONFIG_WPS_STRICT + wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses " + "forbidden NULL termination"); + wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key", + cred->key, cred->key_len); + return -1; +#else /* CONFIG_WPS_STRICT */ /* * A deployed external registrar is known to encode ASCII * passphrases incorrectly. Remove the extra NULL termination @@ -277,7 +291,9 @@ static void wps_workaround_cred_key(struct wps_credential *cred) wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL " "termination from ASCII passphrase"); cred->key_len--; +#endif /* CONFIG_WPS_STRICT */ } + return 0; } @@ -300,12 +316,11 @@ int wps_process_cred(struct wps_parse_attr *attr, wps_process_cred_eap_identity(cred, attr->eap_identity, attr->eap_identity_len) || wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) || - wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled)) + wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) || + wps_process_cred_ap_channel(cred, attr->ap_channel)) return -1; - wps_workaround_cred_key(cred); - - return 0; + return wps_workaround_cred_key(cred); } @@ -324,7 +339,5 @@ int wps_process_ap_settings(struct wps_parse_attr *attr, wps_process_cred_mac_addr(cred, attr->mac_addr)) return -1; - wps_workaround_cred_key(cred); - - return 0; + return wps_workaround_cred_key(cred); } diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c index 6ef14db..68d9f0a 100644 --- a/contrib/wpa/src/wps/wps_common.c +++ b/contrib/wpa/src/wps/wps_common.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - common functionality - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,8 +14,8 @@ #include "crypto/dh_group5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wps_i.h" -#include "wps_dev_attr.h" void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, @@ -81,6 +75,8 @@ int wps_derive_keys(struct wps_data *wps) return -1; } + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); + wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey); dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); dh5_free(wps->dh_ctx); wps->dh_ctx = NULL; @@ -241,7 +237,7 @@ unsigned int wps_generate_pin(void) unsigned int val; /* Generate seven random digits for the PIN */ - if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) { + if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) { struct os_time now; os_get_time(&now); val = os_random() ^ now.sec ^ now.usec; @@ -253,7 +249,24 @@ unsigned int wps_generate_pin(void) } -void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg) +int wps_pin_str_valid(const char *pin) +{ + const char *p; + size_t len; + + p = pin; + while (*p >= '0' && *p <= '9') + p++; + if (*p != '\0') + return 0; + + len = p - pin; + return len == 4 || len == 8; +} + + +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, + u16 config_error, u16 error_indication) { union wps_event_data data; @@ -262,6 +275,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg) os_memset(&data, 0, sizeof(data)); data.fail.msg = msg; + data.fail.config_error = config_error; + data.fail.error_indication = error_indication; wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); } @@ -309,7 +324,7 @@ void wps_pbc_timeout_event(struct wps_context *wps) #ifdef CONFIG_WPS_OOB -static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) +struct wpabuf * wps_get_oob_cred(struct wps_context *wps) { struct wps_data data; struct wpabuf *plain; @@ -325,7 +340,9 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) data.wps = wps; data.auth_type = wps->auth_types; data.encr_type = wps->encr_types; - if (wps_build_version(plain) || wps_build_cred(&data, plain)) { + if (wps_build_version(plain) || + wps_build_cred(&data, plain) || + wps_build_wfa_ext(plain, 0, NULL, 0)) { wpabuf_free(plain); return NULL; } @@ -334,31 +351,22 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) } -static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw) { struct wpabuf *data; - data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password attribute"); + data = wpabuf_alloc(200); + if (data == NULL) return NULL; - } - - wpabuf_free(wps->oob_conf.dev_password); - wps->oob_conf.dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (wps->oob_conf.dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - wpabuf_free(data); - return NULL; - } if (wps_build_version(data) || - wps_build_oob_dev_password(data, wps)) { - wpa_printf(MSG_ERROR, "WPS: Build OOB device password " - "attribute error"); + wps_build_oob_dev_pw(data, dev_pw_id, pubkey, + wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || + wps_build_wfa_ext(data, 0, NULL, 0)) { + wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " + "token"); wpabuf_free(data); return NULL; } @@ -367,66 +375,17 @@ static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) } -static int wps_parse_oob_dev_pwd(struct wps_context *wps, - struct wpabuf *data) -{ - struct oob_conf_data *oob_conf = &wps->oob_conf; - struct wps_parse_attr attr; - const u8 *pos; - - if (wps_parse_msg(data, &attr) < 0 || - attr.oob_dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: OOB device password not found"); - return -1; - } - - pos = attr.oob_dev_password; - - oob_conf->pubkey_hash = - wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN); - if (oob_conf->pubkey_hash == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "public key hash"); - return -1; - } - pos += WPS_OOB_PUBKEY_HASH_LEN; - - wps->oob_dev_pw_id = WPA_GET_BE16(pos); - pos += sizeof(wps->oob_dev_pw_id); - - oob_conf->dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (oob_conf->dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - return -1; - } - wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password, - wpabuf_size(oob_conf->dev_password)), - wpabuf_size(oob_conf->dev_password), pos, - WPS_OOB_DEVICE_PASSWORD_LEN); - - return 0; -} - - -static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr) { struct wpabuf msg; - struct wps_parse_attr attr; size_t i; - if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) { - wpa_printf(MSG_ERROR, "WPS: OOB credential not found"); - return -1; - } - - for (i = 0; i < attr.num_cred; i++) { + for (i = 0; i < attr->num_cred; i++) { struct wps_credential local_cred; struct wps_parse_attr cattr; os_memset(&local_cred, 0, sizeof(local_cred)); - wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]); + wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]); if (wps_parse_msg(&msg, &cattr) < 0 || wps_process_cred(&cattr, &local_cred)) { wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB " @@ -440,94 +399,6 @@ static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) } -int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, - int registrar) -{ - struct wpabuf *data; - int ret, write_f, oob_method = wps->oob_conf.oob_method; - void *oob_priv; - - write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar; - - oob_priv = oob_dev->init_func(wps, oob_dev, registrar); - if (oob_priv == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device"); - return -1; - } - - if (write_f) { - if (oob_method == OOB_METHOD_CRED) - data = wps_get_oob_cred(wps); - else - data = wps_get_oob_dev_pwd(wps); - - ret = 0; - if (data == NULL || oob_dev->write_func(oob_priv, data) < 0) - ret = -1; - } else { - data = oob_dev->read_func(oob_priv); - if (data == NULL) - ret = -1; - else { - if (oob_method == OOB_METHOD_CRED) - ret = wps_parse_oob_cred(wps, data); - else - ret = wps_parse_oob_dev_pwd(wps, data); - } - } - wpabuf_free(data); - oob_dev->deinit_func(oob_priv); - - if (ret < 0) { - wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data"); - return -1; - } - - return 0; -} - - -struct oob_device_data * wps_get_oob_device(char *device_type) -{ -#ifdef CONFIG_WPS_UFD - if (os_strstr(device_type, "ufd") != NULL) - return &oob_ufd_device_data; -#endif /* CONFIG_WPS_UFD */ -#ifdef CONFIG_WPS_NFC - if (os_strstr(device_type, "nfc") != NULL) - return &oob_nfc_device_data; -#endif /* CONFIG_WPS_NFC */ - - return NULL; -} - - -#ifdef CONFIG_WPS_NFC -struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name) -{ - if (device_name == NULL) - return NULL; -#ifdef CONFIG_WPS_NFC_PN531 - if (os_strstr(device_name, "pn531") != NULL) - return &oob_nfc_pn531_device_data; -#endif /* CONFIG_WPS_NFC_PN531 */ - - return NULL; -} -#endif /* CONFIG_WPS_NFC */ - - -int wps_get_oob_method(char *method) -{ - if (os_strstr(method, "pin-e") != NULL) - return OOB_METHOD_DEV_PWD_E; - if (os_strstr(method, "pin-r") != NULL) - return OOB_METHOD_DEV_PWD_R; - if (os_strstr(method, "cred") != NULL) - return OOB_METHOD_CRED; - return OOB_METHOD_UNKNOWN; -} - #endif /* CONFIG_WPS_OOB */ @@ -604,15 +475,13 @@ u16 wps_config_methods_str2bin(const char *str) if (str == NULL) { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS_UFD - methods |= WPS_CONFIG_USBA; -#endif /* CONFIG_WPS_UFD */ +#ifdef CONFIG_WPS2 + methods |= WPS_CONFIG_VIRT_DISPLAY; +#endif /* CONFIG_WPS2 */ #ifdef CONFIG_WPS_NFC methods |= WPS_CONFIG_NFC_INTERFACE; #endif /* CONFIG_WPS_NFC */ } else { - if (os_strstr(str, "usba")) - methods |= WPS_CONFIG_USBA; if (os_strstr(str, "ethernet")) methods |= WPS_CONFIG_ETHERNET; if (os_strstr(str, "label")) @@ -629,7 +498,114 @@ u16 wps_config_methods_str2bin(const char *str) methods |= WPS_CONFIG_PUSHBUTTON; if (os_strstr(str, "keypad")) methods |= WPS_CONFIG_KEYPAD; +#ifdef CONFIG_WPS2 + if (os_strstr(str, "virtual_display")) + methods |= WPS_CONFIG_VIRT_DISPLAY; + if (os_strstr(str, "physical_display")) + methods |= WPS_CONFIG_PHY_DISPLAY; + if (os_strstr(str, "virtual_push_button")) + methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if (os_strstr(str, "physical_push_button")) + methods |= WPS_CONFIG_PHY_PUSHBUTTON; +#endif /* CONFIG_WPS2 */ } return methods; } + + +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_ACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_NACK) || + wps_build_enrollee_nonce(wps, msg) || + wps_build_registrar_nonce(wps, msg) || + wps_build_config_error(msg, wps->config_error) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw) +{ + struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret; + void *dh_ctx; + u16 val; + + pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); + if (pw == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN), + WPS_OOB_DEVICE_PASSWORD_LEN) || + random_get_bytes((u8 *) &val, sizeof(val))) { + wpabuf_free(pw); + return NULL; + } + + dh_ctx = dh5_init(&priv, &pub); + if (dh_ctx == NULL) { + wpabuf_free(pw); + return NULL; + } + dh5_free(dh_ctx); + + *id = 0x10 + val % 0xfff0; + wpabuf_free(*pubkey); + *pubkey = pub; + wpabuf_free(*privkey); + *privkey = priv; + wpabuf_free(*dev_pw); + *dev_pw = pw; + + ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h index 750ca41..2f42603 100644 --- a/contrib/wpa/src/wps/wps_defs.h +++ b/contrib/wpa/src/wps/wps_defs.h @@ -2,20 +2,28 @@ * Wi-Fi Protected Setup - message definitions * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEFS_H #define WPS_DEFS_H +#ifdef CONFIG_WPS_TESTING + +extern int wps_version_number; +extern int wps_testing_dummy_cred; +#define WPS_VERSION wps_version_number + +#else /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_WPS2 +#define WPS_VERSION 0x20 +#else /* CONFIG_WPS2 */ #define WPS_VERSION 0x10 +#endif /* CONFIG_WPS2 */ + +#endif /* CONFIG_WPS_TESTING */ /* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */ #define WPS_DH_GROUP 5 @@ -33,7 +41,7 @@ #define WPS_MGMTAUTHKEY_LEN 32 #define WPS_MGMTENCKEY_LEN 16 #define WPS_MGMT_KEY_ID_LEN 16 -#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54 +#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 @@ -124,7 +132,20 @@ enum wps_attribute { ATTR_KEY_PROVIDED_AUTO = 0x1061, ATTR_802_1X_ENABLED = 0x1062, ATTR_APPSESSIONKEY = 0x1063, - ATTR_WEPTRANSMITKEY = 0x1064 + ATTR_WEPTRANSMITKEY = 0x1064, + ATTR_REQUESTED_DEV_TYPE = 0x106a, + ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */ +}; + +#define WPS_VENDOR_ID_WFA 14122 + +/* WFA Vendor Extension subelements */ +enum { + WFA_ELEM_VERSION2 = 0x00, + WFA_ELEM_AUTHORIZEDMACS = 0x01, + WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, + WFA_ELEM_REQUEST_TO_ENROLL = 0x03, + WFA_ELEM_SETTINGS_DELAY_TIME = 0x04 }; /* Device Password ID */ @@ -197,6 +218,14 @@ enum wps_config_error { WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18 }; +/* Vendor specific Error Indication for WPS event messages */ +enum wps_error_indication { + WPS_EI_NO_ERROR, + WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED, + WPS_EI_SECURITY_WEP_PROHIBITED, + NUM_WPS_EI_VALUES +}; + /* RF Bands */ #define WPS_RF_24GHZ 0x01 #define WPS_RF_50GHZ 0x02 @@ -211,6 +240,12 @@ enum wps_config_error { #define WPS_CONFIG_NFC_INTERFACE 0x0040 #define WPS_CONFIG_PUSHBUTTON 0x0080 #define WPS_CONFIG_KEYPAD 0x0100 +#ifdef CONFIG_WPS2 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 +#endif /* CONFIG_WPS2 */ /* Connection Type Flags */ #define WPS_CONN_ESS 0x01 @@ -290,4 +325,6 @@ enum wps_response_type { /* Walk Time for push button configuration (in seconds) */ #define WPS_PBC_WALK_TIME 120 +#define WPS_MAX_AUTHORIZED_MACS 5 + #endif /* WPS_DEFS_H */ diff --git a/contrib/wpa/src/wps/wps_dev_attr.c b/contrib/wpa/src/wps/wps_dev_attr.c index 090bfa2..3c94a43 100644 --- a/contrib/wpa/src/wps/wps_dev_attr.c +++ b/contrib/wpa/src/wps/wps_dev_attr.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,71 +13,74 @@ #include "wps_dev_attr.h" -static int wps_build_manufacturer(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Manufacturer"); wpabuf_put_be16(msg, ATTR_MANUFACTURER); len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->manufacturer, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->manufacturer, len); return 0; } -static int wps_build_model_name(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Model Name"); wpabuf_put_be16(msg, ATTR_MODEL_NAME); len = dev->model_name ? os_strlen(dev->model_name) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->model_name, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_name, len); return 0; } -static int wps_build_model_number(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Model Number"); wpabuf_put_be16(msg, ATTR_MODEL_NUMBER); len = dev->model_number ? os_strlen(dev->model_number) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->model_number, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->model_number, len); return 0; } @@ -95,18 +92,20 @@ static int wps_build_serial_number(struct wps_device_data *dev, wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); len = dev->serial_number ? os_strlen(dev->serial_number) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->serial_number, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->serial_number, len); return 0; } @@ -121,24 +120,62 @@ int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) } -static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) +int wps_build_secondary_dev_type(struct wps_device_data *dev, + struct wpabuf *msg) +{ + if (!dev->num_sec_dev_types) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type"); + wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST); + wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); + wpabuf_put_data(msg, dev->sec_dev_type, + WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); + + return 0; +} + + +int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, + unsigned int num_req_dev_types, + const u8 *req_dev_types) +{ + unsigned int i; + + for (i = 0; i < num_req_dev_types; i++) { + wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type", + req_dev_types + i * WPS_DEV_TYPE_LEN, + WPS_DEV_TYPE_LEN); + wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE); + wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); + wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN, + WPS_DEV_TYPE_LEN); + } + + return 0; +} + + +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Device Name"); wpabuf_put_be16(msg, ATTR_DEV_NAME); len = dev->device_name ? os_strlen(dev->device_name) : 0; +#ifndef CONFIG_WPS_STRICT if (len == 0) { /* * Some deployed WPS implementations fail to parse zero-length - * attributes. As a workaround, send a null character if the + * attributes. As a workaround, send a space character if the * device attribute string is empty. */ wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, '\0'); - } else { - wpabuf_put_be16(msg, len); - wpabuf_put_data(msg, dev->device_name, len); + wpabuf_put_u8(msg, ' '); + return 0; } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(msg, len); + wpabuf_put_data(msg, dev->device_name, len); return 0; } @@ -166,6 +203,20 @@ int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) } +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) +{ + if (dev->vendor_ext_m1 != NULL) { + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", + wpabuf_head_u8(dev->vendor_ext_m1), + wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_buf(msg, dev->vendor_ext_m1); + } + return 0; +} + + int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); @@ -176,6 +227,25 @@ int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) } +int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (dev->vendor_ext[i] == NULL) + continue; + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension", + wpabuf_head_u8(dev->vendor_ext[i]), + wpabuf_len(dev->vendor_ext[i])); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i])); + wpabuf_put_buf(msg, dev->vendor_ext[i]); + } + + return 0; +} + + static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, size_t str_len) { diff --git a/contrib/wpa/src/wps/wps_dev_attr.h b/contrib/wpa/src/wps/wps_dev_attr.h index a9c16ea..200c9c4 100644 --- a/contrib/wpa/src/wps/wps_dev_attr.h +++ b/contrib/wpa/src/wps/wps_dev_attr.h @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEV_ATTR_H @@ -17,11 +11,19 @@ struct wps_parse_attr; +int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_secondary_dev_type(struct wps_device_data *dev, + struct wpabuf *msg); +int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); @@ -29,5 +31,9 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); void wps_device_data_dup(struct wps_device_data *dst, const struct wps_device_data *src); void wps_device_data_free(struct wps_device_data *dev); +int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, + unsigned int num_req_dev_types, + const u8 *req_dev_types); #endif /* WPS_DEV_ATTR_H */ diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c index dff24d4..837b941 100644 --- a/contrib/wpa/src/wps/wps_enrollee.c +++ b/contrib/wpa/src/wps/wps_enrollee.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - Enrollee * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wps_i.h" #include "wps_dev_attr.h" @@ -53,7 +48,7 @@ static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg) const u8 *addr[4]; size_t len[4]; - if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: E-S2", @@ -119,8 +114,9 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; + u16 config_methods; - if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0) + if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce", wps->nonce_e, WPS_NONCE_LEN); @@ -130,6 +126,26 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) if (msg == NULL) return NULL; + config_methods = wps->wps->config_methods; + if (wps->wps->ap && !wps->pbc_in_m1 && + (wps->dev_password_len != 0 || + (config_methods & WPS_CONFIG_DISPLAY))) { + /* + * These are the methods that the AP supports as an Enrollee + * for adding external Registrars, so remove PushButton. + * + * As a workaround for Windows 7 mechanism for probing WPS + * capabilities from M1, leave PushButton option if no PIN + * method is available or if WPS configuration enables PBC + * workaround. + */ + config_methods &= ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ + } + if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -139,14 +155,16 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || - wps_build_config_methods(msg, wps->wps->config_methods) || + wps_build_config_methods(msg, config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg) || wps_build_assoc_state(wps, msg) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || - wps_build_os_version(&wps->wps->dev, msg)) { + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; } @@ -176,6 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -208,6 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -232,20 +252,47 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Authentication Type"); + u16 auth_type = wps->wps->auth_types; + + /* Select the best authentication type */ + if (auth_type & WPS_AUTH_WPA2PSK) + auth_type = WPS_AUTH_WPA2PSK; + else if (auth_type & WPS_AUTH_WPAPSK) + auth_type = WPS_AUTH_WPAPSK; + else if (auth_type & WPS_AUTH_OPEN) + auth_type = WPS_AUTH_OPEN; + else if (auth_type & WPS_AUTH_SHARED) + auth_type = WPS_AUTH_SHARED; + + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); wpabuf_put_be16(msg, ATTR_AUTH_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->auth_types); + wpabuf_put_be16(msg, auth_type); return 0; } static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Encryption Type"); + u16 encr_type = wps->wps->encr_types; + + /* Select the best encryption type */ + if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + if (encr_type & WPS_ENCR_AES) + encr_type = WPS_ENCR_AES; + else if (encr_type & WPS_ENCR_TKIP) + encr_type = WPS_ENCR_TKIP; + } else { + if (encr_type & WPS_ENCR_WEP) + encr_type = WPS_ENCR_WEP; + else if (encr_type & WPS_ENCR_NONE) + encr_type = WPS_ENCR_NONE; + } + + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); wpabuf_put_be16(msg, ATTR_ENCR_TYPE); wpabuf_put_be16(msg, 2); - wpabuf_put_be16(msg, wps->wps->encr_types); + wpabuf_put_be16(msg, encr_type); return 0; } @@ -310,6 +357,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -345,7 +393,8 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { + wps_build_registrar_nonce(wps, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { wpabuf_free(msg); return NULL; } @@ -360,51 +409,6 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) } -static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_ACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - -static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_NACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg) || - wps_build_config_error(msg, wps->config_error)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { @@ -519,23 +523,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id != DEV_PW_DEFAULT && - wps->wps->oob_conf.pubkey_hash) { - const u8 *addr[1]; - u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), - WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); - return -1; - } - } -#endif /* CONFIG_WPS_OOB */ - wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) @@ -657,10 +644,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, - size_t cred_len) + size_t cred_len, int wps2) { struct wps_parse_attr attr; struct wpabuf msg; + int ret = 0; wpa_printf(MSG_DEBUG, "WPS: Received Credential"); os_memset(&wps->cred, 0, sizeof(wps->cred)); @@ -682,24 +670,48 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ } +#ifdef CONFIG_WPS2 + if (!(wps->cred.encr_type & + (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { + if (wps->cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject Credential " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -2; + } + + wpa_printf(MSG_INFO, "WPS: Reject Credential due to " + "invalid encr_type 0x%x", wps->cred.encr_type); + return -1; + } +#endif /* CONFIG_WPS2 */ + if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); wps->cred.cred_attr = NULL; wps->cred.cred_attr_len = 0; } - return 0; + return ret; } static int wps_process_creds(struct wps_data *wps, const u8 *cred[], - size_t cred_len[], size_t num_cred) + size_t cred_len[], size_t num_cred, int wps2) { size_t i; + int ok = 0; if (wps->wps->ap) return 0; @@ -711,17 +723,29 @@ static int wps_process_creds(struct wps_data *wps, const u8 *cred[], } for (i = 0; i < num_cred; i++) { - if (wps_process_cred_e(wps, cred[i], cred_len[i])) + int res; + res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2); + if (res == 0) + ok++; + else if (res == -2) + wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped"); + else return -1; } + if (ok == 0) { + wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute " + "received"); + return -1; + } + return 0; } static int wps_process_ap_settings_e(struct wps_data *wps, struct wps_parse_attr *attr, - struct wpabuf *attrs) + struct wpabuf *attrs, int wps2) { struct wps_credential cred; @@ -747,7 +771,61 @@ static int wps_process_ap_settings_e(struct wps_data *wps, * reasons, allow this to be processed since we do not really * use the MAC Address information for anything. */ +#ifdef CONFIG_WPS_STRICT + if (wps2) { + wpa_printf(MSG_INFO, "WPS: Do not accept incorrect " + "MAC Address in AP Settings"); + return -1; + } +#endif /* CONFIG_WPS_STRICT */ + } + +#ifdef CONFIG_WPS2 + if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) + { + if (cred.encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject new AP settings " + "due to WEP configuration"); + wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED; + return -1; + } + + wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " + "invalid encr_type 0x%x", cred.encr_type); + return -1; + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_WPS_STRICT + if (wps2) { + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == + WPS_ENCR_TKIP || + (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 " + "AP Settings: WPA-Personal/TKIP only"); + wps->error_indication = + WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED; + return -1; + } + } +#endif /* CONFIG_WPS_STRICT */ + +#ifdef CONFIG_WPS2 + if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) + { + wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " + "TKIP+AES"); + cred.encr_type |= WPS_ENCR_AES; + } + + if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " + "WPAPSK+WPA2PSK"); + cred.auth_type |= WPS_AUTH_WPA2PSK; } +#endif /* CONFIG_WPS2 */ if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); @@ -779,8 +857,15 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, return WPS_CONTINUE; } + /* + * Stop here on an AP as an Enrollee if AP Setup is locked unless the + * special locked mode is used to allow protocol run up to M7 in order + * to support external Registrars that only learn the current AP + * configuration without changing it. + */ if (wps->wps->ap && - (wps->wps->ap_setup_locked || wps->dev_password == NULL)) { + ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) || + wps->dev_password == NULL)) { wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " "registration of a new Registrar"); wps->config_error = WPS_CFG_SETUP_LOCKED; @@ -888,6 +973,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -935,6 +1026,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -946,6 +1043,10 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, } wpabuf_free(decrypted); + if (wps->wps->ap) + wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, + NULL); + wps->state = SEND_M7; return WPS_CONTINUE; } @@ -973,6 +1074,19 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->wps->ap && wps->wps->ap_setup_locked) { + /* + * Stop here if special ap_setup_locked == 2 mode allowed the + * protocol to continue beyond M2. This allows ER to learn the + * current AP settings without changing them. + */ + wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " + "registration of a new Registrar"); + wps->config_error = WPS_CFG_SETUP_LOCKED; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { @@ -982,13 +1096,21 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m8_encr(decrypted, wps->wps->ap, + attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_creds(wps, eattr.cred, eattr.cred_len, - eattr.num_cred) || - wps_process_ap_settings_e(wps, &eattr, decrypted)) { + eattr.num_cred, attr->version2 != NULL) || + wps_process_ap_settings_e(wps, &eattr, decrypted, + attr->version2 != NULL)) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -1011,44 +1133,52 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); - return WPS_FAILURE; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; } switch (*attr.msg_type) { case WPS_M2: + if (wps_validate_m2(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2(wps, msg, &attr); break; case WPS_M2D: + if (wps_validate_m2d(msg) < 0) + return WPS_FAILURE; ret = wps_process_m2d(wps, &attr); break; case WPS_M4: + if (wps_validate_m4(msg) < 0) + return WPS_FAILURE; ret = wps_process_m4(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M4); + wps_fail_event(wps->wps, WPS_M4, wps->config_error, + wps->error_indication); break; case WPS_M6: + if (wps_validate_m6(msg) < 0) + return WPS_FAILURE; ret = wps_process_m6(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M6); + wps_fail_event(wps->wps, WPS_M6, wps->config_error, + wps->error_indication); break; case WPS_M8: + if (wps_validate_m8(msg) < 0) + return WPS_FAILURE; ret = wps_process_m8(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M8); + wps_fail_event(wps->wps, WPS_M8, wps->config_error, + wps->error_indication); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -1084,12 +1214,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1102,14 +1226,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1130,18 +1254,13 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, const struct wpabuf *msg) { struct wps_parse_attr attr; + u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -1154,7 +1273,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", @@ -1165,7 +1284,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", attr.enrollee_nonce, WPS_NONCE_LEN); @@ -1180,18 +1299,22 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, return WPS_FAILURE; } + config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with " - "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + "Configuration Error %d", config_error); switch (wps->state) { case RECV_M4: - wps_fail_event(wps->wps, WPS_M3); + wps_fail_event(wps->wps, WPS_M3, config_error, + wps->error_indication); break; case RECV_M6: - wps_fail_event(wps->wps, WPS_M5); + wps_fail_event(wps->wps, WPS_M5, config_error, + wps->error_indication); break; case RECV_M8: - wps_fail_event(wps->wps, WPS_M7); + wps_fail_event(wps->wps, WPS_M7, config_error, + wps->error_indication); break; default: break; @@ -1230,8 +1353,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, case WSC_UPnP: return wps_process_wsc_msg(wps, msg); case WSC_ACK: + if (wps_validate_wsc_ack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: + if (wps_validate_wsc_nack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); default: wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code); diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c index e0cdd1d..95a0dec 100644 --- a/contrib/wpa/src/wps/wps_er.c +++ b/contrib/wpa/src/wps/wps_er.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -62,11 +56,15 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, } -static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) +static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr, + const u8 *uuid) { struct wps_er_sta *sta; dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { - if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0) + if ((addr == NULL || + os_memcmp(sta->addr, addr, ETH_ALEN) == 0) && + (uuid == NULL || + os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0)) return sta; } return NULL; @@ -275,6 +273,64 @@ fail: wps_er_ap_unsubscribed(ap->er, ap); } + +static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er, + const u8 *uuid) +{ + struct wps_er_ap_settings *s; + dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list) + if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0) + return s; + return NULL; +} + + +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) +{ + struct wps_er_ap *ap; + struct wps_er_ap_settings *settings; + + ap = wps_er_ap_get(er, addr, NULL); + if (ap == NULL || ap->ap_settings == NULL) + return -1; + + settings = wps_er_ap_get_settings(er, ap->uuid); + if (!settings) { + settings = os_zalloc(sizeof(*settings)); + if (settings == NULL) + return -1; + os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN); + dl_list_add(&er->ap_settings, &settings->list); + } + os_memcpy(&settings->ap_settings, ap->ap_settings, + sizeof(struct wps_credential)); + + return 0; +} + + +static int wps_er_ap_use_cached_settings(struct wps_er *er, + struct wps_er_ap *ap) +{ + struct wps_er_ap_settings *s; + + if (ap->ap_settings) + return 0; + + s = wps_er_ap_get_settings(ap->er, ap->uuid); + if (!s) + return -1; + + ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); + if (ap->ap_settings == NULL) + return -1; + + os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); + wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); + return 0; +} + + static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) { wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", @@ -352,6 +408,7 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); ap->subscribed = 1; wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); + wps_er_ap_use_cached_settings(ap->er, ap); wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); break; case HTTP_CLIENT_FAILED: @@ -439,16 +496,61 @@ static void wps_er_get_device_info(struct wps_er_ap *ap) } +static const char * wps_er_find_wfadevice(const char *data) +{ + const char *tag, *tagname, *end; + char *val; + int found = 0; + + while (!found) { + /* Find next <device> */ + for (;;) { + if (xml_next_tag(data, &tag, &tagname, &end)) + return NULL; + data = end; + if (!os_strncasecmp(tagname, "device", 6) && + *tag != '/' && + (tagname[6] == '>' || !isgraph(tagname[6]))) { + break; + } + } + + /* Check whether deviceType is WFADevice */ + val = xml_get_first_item(data, "deviceType"); + if (val == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val); + found = os_strcasecmp(val, "urn:schemas-wifialliance-org:" + "device:WFADevice:1") == 0; + os_free(val); + } + + return data; +} + + static void wps_er_parse_device_description(struct wps_er_ap *ap, struct wpabuf *reply) { /* Note: reply includes null termination after the buffer data */ - const char *data = wpabuf_head(reply); + const char *tmp, *data = wpabuf_head(reply); char *pos; wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", wpabuf_head(reply), wpabuf_len(reply)); + /* + * The root device description may include multiple devices, so first + * find the beginning of the WFADevice description to allow the + * simplistic parser to pick the correct entries. + */ + tmp = wps_er_find_wfadevice(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - " + "trying to parse invalid data"); + } else + data = tmp; + ap->friendly_name = xml_get_first_item(data, "friendlyName"); wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); @@ -583,8 +685,12 @@ void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) static void wps_er_ap_remove_all(struct wps_er *er) { struct wps_er_ap *prev, *ap; + struct wps_er_ap_settings *prev_s, *s; dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) wps_er_ap_remove_entry(er, ap); + dl_list_for_each_safe(s, prev_s, &er->ap_settings, + struct wps_er_ap_settings, list) + os_free(s); } @@ -649,7 +755,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, struct wps_parse_attr *attr, int probe_req) { - struct wps_er_sta *sta = wps_er_sta_get(ap, addr); + struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL); int new_sta = 0; int m1; @@ -756,6 +862,12 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " "(TLVs from Probe Request)", msg); + if (wps_validate_probe_req(msg, addr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied " + "Probe Request frame from " MACSTR, MAC2STR(addr)); + return; + } + if (wps_parse_msg(msg, &attr) < 0) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " "WLANEvent message"); @@ -763,6 +875,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, } wps_er_add_sta_data(ap, addr, &attr, 1); + wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); } @@ -1151,7 +1264,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req) struct wps_er * -wps_er_init(struct wps_context *wps, const char *ifname) +wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) { struct wps_er *er; struct in_addr addr; @@ -1161,6 +1274,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) return NULL; dl_list_init(&er->ap); dl_list_init(&er->ap_unsubscribing); + dl_list_init(&er->ap_settings); er->multicast_sd = -1; er->ssdp_sd = -1; @@ -1175,6 +1289,16 @@ wps_er_init(struct wps_context *wps, const char *ifname) /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter) { + if (inet_aton(filter, &er->filter_addr) == 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " + "address %s", filter); + wps_er_deinit(er, NULL, NULL); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " + "with %s", filter); + } if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " @@ -1184,6 +1308,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) } if (wps_er_ssdp_init(er) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1191,6 +1316,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) addr.s_addr = er->ip_addr; er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); if (er->http_srv == NULL) { + wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1256,19 +1382,30 @@ static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE; + data.set_sel_reg.uuid = ap->uuid; break; case HTTP_CLIENT_FAILED: case HTTP_CLIENT_INVALID_REPLY: case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED; + data.set_sel_reg.uuid = ap->uuid; break; } http_client_free(ap->http); ap->http = NULL; + + if (data.set_sel_reg.uuid) + ap->er->wps->event_cb(ap->er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); } @@ -1290,6 +1427,12 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) return; } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - " + "skip SetSelectedRegistrar"); + return; + } + url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); @@ -1339,26 +1482,74 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, } +static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) +{ +#ifdef CONFIG_WPS2 + wpabuf_put_be16(msg, ATTR_UUID_R); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); +#endif /* CONFIG_WPS2 */ + return 0; +} + + void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { struct wpabuf *msg; struct wps_er_ap *ap; + struct wps_registrar *reg = er->wps->registrar; + const u8 *auth_macs; +#ifdef CONFIG_WPS2 + u8 bcast[ETH_ALEN]; +#endif /* CONFIG_WPS2 */ + size_t count; + union wps_event_data data; + + if (er->skip_set_sel_reg) { + wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); + return; + } msg = wpabuf_alloc(500); if (msg == NULL) return; + auth_macs = wps_authorized_macs(reg, &count); +#ifdef CONFIG_WPS2 + if (count == 0) { + os_memset(bcast, 0xff, ETH_ALEN); + auth_macs = bcast; + count = 1; + } +#endif /* CONFIG_WPS2 */ + if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || - wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) { + wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || + wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; } - dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) + os_memset(&data, 0, sizeof(data)); + data.set_sel_reg.sel_reg = sel_reg; + data.set_sel_reg.dev_passwd_id = dev_passwd_id; + data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods; + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START; + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if (er->set_sel_reg_uuid_filter && + os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter, + WPS_UUID_LEN) != 0) + continue; + data.set_sel_reg.uuid = ap->uuid; + er->wps->event_cb(er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); wps_er_send_set_sel_reg(ap, msg); + } wpabuf_free(msg); } @@ -1366,16 +1557,41 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, int wps_er_pbc(struct wps_er *er, const u8 *uuid) { + int res; + struct wps_er_ap *ap; + if (er == NULL || er->wps == NULL) return -1; - /* - * TODO: Should enable PBC mode only in a single AP based on which AP - * the Enrollee (uuid) is using. Now, we may end up enabling multiple - * APs in PBC mode which could result in session overlap at the - * Enrollee. - */ - if (wps_registrar_button_pushed(er->wps->registrar)) + if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { + wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " + "mode"); + return -2; + } + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + struct wps_er_sta *sta = NULL; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + sta = wps_er_sta_get(ap, NULL, uuid); + if (sta) { + uuid = ap->uuid; + break; + } + } + if (sta == NULL) + return -3; /* Unknown UUID */ + } + + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known"); + return -4; + } + + er->set_sel_reg_uuid_filter = uuid; + res = wps_registrar_button_pushed(er->wps->registrar, NULL); + er->set_sel_reg_uuid_filter = NULL; + if (res) return -1; return 0; @@ -1385,6 +1601,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); os_free(ap->ap_settings); ap->ap_settings = os_malloc(sizeof(*cred)); @@ -1393,7 +1611,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) ap->ap_settings->cred_attr = NULL; } - /* TODO: send info through ctrl_iface */ + os_memset(&data, 0, sizeof(data)); + data.ap_settings.uuid = ap->uuid; + data.ap_settings.cred = cred; + ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, + &data); } @@ -1436,6 +1658,8 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, if (buf == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " "NewOutMessage from PutMessage response"); + wps_deinit(ap->wps); + ap->wps = NULL; return; } wps_er_ap_process(ap, buf); @@ -1487,10 +1711,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) { enum wps_process_res res; + struct wps_parse_attr attr; + enum wsc_op_code op_code; - res = wps_process_msg(ap->wps, WSC_MSG, msg); + op_code = WSC_MSG; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + switch (*attr.msg_type) { + case WPS_WSC_ACK: + op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + op_code = WSC_NACK; + break; + case WPS_WSC_DONE: + op_code = WSC_Done; + break; + } + } + + res = wps_process_msg(ap->wps, op_code, msg); if (res == WPS_CONTINUE) { - enum wsc_op_code op_code; struct wpabuf *next = wps_get_msg(ap->wps, &op_code); if (next) { wps_er_ap_put_message(ap, next); @@ -1501,6 +1741,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) wps_deinit(ap->wps); ap->wps = NULL; } + } else if (res == WPS_DONE) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); + wps_deinit(ap->wps); + ap->wps = NULL; } else { wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " "AP (res=%d)", res); @@ -1656,8 +1900,137 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) return -1; - /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; + + return 0; +} + + +int wps_er_set_config(struct wps_er *er, const u8 *uuid, + const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " + "request"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " + "config request"); + + return 0; +} + + +static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_config cfg; + + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " + "progress with this AP"); + return; + } + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = ap->er->wps; + cfg.registrar = 1; + cfg.new_ap_settings = ap->ap_settings; + ap->wps = wps_init(&cfg); + if (ap->wps == NULL) + return; + ap->wps->ap_settings_cb = NULL; + ap->wps->ap_settings_cb_ctx = NULL; + + wps_er_ap_process(ap, m1); +} + + +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len, const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start config"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + + if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) + return -1; + + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; return 0; } + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid) +{ + struct wps_er_ap *ap; + struct wpabuf *ret; + struct wps_data data; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = er->wps; + data.use_cred = ap->ap_settings; + if (wps_build_version(ret) || + wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ diff --git a/contrib/wpa/src/wps/wps_er.h b/contrib/wpa/src/wps/wps_er.h index b13b950..6119647 100644 --- a/contrib/wpa/src/wps/wps_er.h +++ b/contrib/wpa/src/wps/wps_er.h @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_ER_H @@ -73,6 +67,12 @@ struct wps_er_ap { void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1); }; +struct wps_er_ap_settings { + struct dl_list list; + u8 uuid[WPS_UUID_LEN]; + struct wps_credential ap_settings; +}; + struct wps_er { struct wps_context *wps; char ifname[17]; @@ -83,6 +83,7 @@ struct wps_er { int ssdp_sd; struct dl_list ap; struct dl_list ap_unsubscribing; + struct dl_list ap_settings; struct http_server *http_srv; int http_port; unsigned int next_ap_id; @@ -90,6 +91,9 @@ struct wps_er { int deinitializing; void (*deinit_done_cb)(void *ctx); void *deinit_done_ctx; + struct in_addr filter_addr; + int skip_set_sel_reg; + const u8 *set_sel_reg_uuid_filter; }; @@ -97,6 +101,7 @@ struct wps_er { void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, const char *location, int max_age); void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr); +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr); /* wps_er_ssdp.c */ int wps_er_ssdp_init(struct wps_er *er); diff --git a/contrib/wpa/src/wps/wps_er_ssdp.c b/contrib/wpa/src/wps/wps_er_ssdp.c index f108435..f9f6e6c 100644 --- a/contrib/wpa/src/wps/wps_er_ssdp.c +++ b/contrib/wpa/src/wps/wps_er_ssdp.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar (SSDP) * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -41,6 +35,9 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) if (nread <= 0) return; buf[nread] = '\0'; + if (er->filter_addr.s_addr && + er->filter_addr.s_addr != addr.sin_addr.s_addr) + return; wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s", inet_ntoa(addr.sin_addr)); @@ -110,6 +107,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) return; /* Not WPS advertisement/reply */ if (byebye) { + wps_er_ap_cache_settings(er, &addr.sin_addr); wps_er_ap_remove(er, &addr.sin_addr); return; } @@ -162,16 +160,25 @@ void wps_er_send_ssdp_msearch(struct wps_er *er) int wps_er_ssdp_init(struct wps_er *er) { - if (add_ssdp_network(er->ifname)) + if (add_ssdp_network(er->ifname)) { + wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for " + "SSDP"); return -1; + } er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); - if (er->multicast_sd < 0) + if (er->multicast_sd < 0) { + wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket " + "for SSDP"); return -1; + } er->ssdp_sd = ssdp_listener_open(); - if (er->ssdp_sd < 0) + if (er->ssdp_sd < 0) { + wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener " + "socket"); return -1; + } if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ, wps_er_ssdp_rx, er, NULL) || diff --git a/contrib/wpa/src/wps/wps_i.h b/contrib/wpa/src/wps/wps_i.h index 50e66f6..8110894 100644 --- a/contrib/wpa/src/wps/wps_i.h +++ b/contrib/wpa/src/wps/wps_i.h @@ -1,21 +1,18 @@ /* * Wi-Fi Protected Setup - internal definitions - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_I_H #define WPS_I_H #include "wps.h" +#include "wps_attr_parse.h" + +struct wps_nfc_pw_token; /** * struct wps_data - WPS registration protocol data @@ -102,6 +99,7 @@ struct wps_data { * config_error - Configuration Error value to be used in NACK */ u16 config_error; + u16 error_indication; int ext_reg; int int_reg; @@ -116,84 +114,14 @@ struct wps_data { struct wps_credential *use_cred; int use_psk_key; -}; - + u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or + * 00:00:00:00:00:00 if not a P2p client */ + int pbc_in_m1; -struct wps_parse_attr { - /* fixed length fields */ - const u8 *version; /* 1 octet */ - const u8 *msg_type; /* 1 octet */ - const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ - const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ - const u8 *auth_type_flags; /* 2 octets */ - const u8 *encr_type_flags; /* 2 octets */ - const u8 *conn_type_flags; /* 1 octet */ - const u8 *config_methods; /* 2 octets */ - const u8 *sel_reg_config_methods; /* 2 octets */ - const u8 *primary_dev_type; /* 8 octets */ - const u8 *rf_bands; /* 1 octet */ - const u8 *assoc_state; /* 2 octets */ - const u8 *config_error; /* 2 octets */ - const u8 *dev_password_id; /* 2 octets */ - const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54) - * octets */ - const u8 *os_version; /* 4 octets */ - const u8 *wps_state; /* 1 octet */ - const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ - const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ - const u8 *auth_type; /* 2 octets */ - const u8 *encr_type; /* 2 octets */ - const u8 *network_idx; /* 1 octet */ - const u8 *network_key_idx; /* 1 octet */ - const u8 *mac_addr; /* ETH_ALEN (6) octets */ - const u8 *key_prov_auto; /* 1 octet (Bool) */ - const u8 *dot1x_enabled; /* 1 octet (Bool) */ - const u8 *selected_registrar; /* 1 octet (Bool) */ - const u8 *request_type; /* 1 octet */ - const u8 *response_type; /* 1 octet */ - const u8 *ap_setup_locked; /* 1 octet */ - - /* variable length fields */ - const u8 *manufacturer; - size_t manufacturer_len; - const u8 *model_name; - size_t model_name_len; - const u8 *model_number; - size_t model_number_len; - const u8 *serial_number; - size_t serial_number_len; - const u8 *dev_name; - size_t dev_name_len; - const u8 *public_key; - size_t public_key_len; - const u8 *encr_settings; - size_t encr_settings_len; - const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; - const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; - const u8 *eap_type; /* <= 8 octets */ - size_t eap_type_len; - const u8 *eap_identity; /* <= 64 octets */ - size_t eap_identity_len; - - /* attributes that can occur multiple times */ -#define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; + struct wps_nfc_pw_token *nfc_pw_token; }; + /* wps_common.c */ void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); @@ -202,18 +130,15 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); -void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg); +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, + u16 config_error, u16 error_indication); void wps_success_event(struct wps_context *wps); void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); void wps_pbc_overlap_event(struct wps_context *wps); void wps_pbc_timeout_event(struct wps_context *wps); -extern struct oob_device_data oob_ufd_device_data; -extern struct oob_device_data oob_nfc_device_data; -extern struct oob_nfc_device_data oob_nfc_pn531_device_data; - -/* wps_attr_parse.c */ -int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); /* wps_attr_build.c */ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); @@ -228,6 +153,8 @@ int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, struct wpabuf *plain); int wps_build_version(struct wpabuf *msg); +int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, + const u8 *auth_macs, size_t auth_macs_count); int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); @@ -235,7 +162,10 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps); +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len); +struct wpabuf * wps_ie_encapsulate(struct wpabuf *data); /* wps_attr_process.c */ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, @@ -264,15 +194,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid); void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); - -/* ndef.c */ -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); -struct wpabuf * ndef_build_wifi(struct wpabuf *buf); - -static inline int wps_version_supported(const u8 *version) -{ - /* Require major version match, but allow minor version differences */ - return version && (*version & 0xf0) == (WPS_VERSION & 0xf0); -} +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count); +int wps_registrar_pbc_overlap(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e); +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token); #endif /* WPS_I_H */ diff --git a/contrib/wpa/src/wps/wps_nfc.c b/contrib/wpa/src/wps/wps_nfc.c deleted file mode 100644 index ff12000..0000000 --- a/contrib/wpa/src/wps/wps_nfc.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * NFC routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - - -struct wps_nfc_data { - struct oob_nfc_device_data *oob_nfc_dev; -}; - - -static void * init_nfc(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - struct oob_nfc_device_data *oob_nfc_dev; - struct wps_nfc_data *data; - - oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name); - if (oob_nfc_dev == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)", - oob_dev->device_name); - return NULL; - } - - if (oob_nfc_dev->init_func(oob_dev->device_path) < 0) - return NULL; - - data = os_zalloc(sizeof(*data)); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc data area"); - return NULL; - } - data->oob_nfc_dev = oob_nfc_dev; - return data; -} - - -static struct wpabuf * read_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi, *buf; - char *raw_data; - size_t len; - - raw_data = data->oob_nfc_dev->read_func(&len); - if (raw_data == NULL) - return NULL; - - wifi = wpabuf_alloc_copy(raw_data, len); - os_free(raw_data); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc read area"); - return NULL; - } - - buf = ndef_parse_wifi(wifi); - wpabuf_free(wifi); - if (buf == NULL) - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap"); - return buf; -} - - -static int write_nfc(void *priv, struct wpabuf *buf) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi; - int ret; - - wifi = ndef_build_wifi(buf); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap"); - return -1; - } - - ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi), - wpabuf_len(wifi)); - wpabuf_free(wifi); - return ret; -} - - -static void deinit_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - - data->oob_nfc_dev->deinit_func(); - - os_free(data); -} - - -struct oob_device_data oob_nfc_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_nfc, - .read_func = read_nfc, - .write_func = write_nfc, - .deinit_func = deinit_nfc, -}; diff --git a/contrib/wpa/src/wps/wps_nfc_pn531.c b/contrib/wpa/src/wps/wps_nfc_pn531.c deleted file mode 100644 index 7e05e4d..0000000 --- a/contrib/wpa/src/wps/wps_nfc_pn531.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * NFC PN531 routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - -#include "WpsNfcType.h" -#include "WpsNfc.h" - - -static int init_nfc_pn531(char *path) -{ - u32 ret; - - ret = WpsNfcInit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize " - "NFC Library: 0x%08x", ret); - return -1; - } - - ret = WpsNfcOpenDevice((int8 *) path); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open " - "NFC Device(%s): 0x%08x", path, ret); - goto fail; - } - - ret = WpsNfcTokenDiscovery(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover " - "token: 0x%08x", ret); - WpsNfcCloseDevice(); - goto fail; - } - - return 0; - -fail: - WpsNfcDeinit(); - return -1; -} - - -static void * read_nfc_pn531(size_t *size) -{ - uint32 len; - u32 ret; - int8 *data; - - ret = WpsNfcRawReadToken(&data, &len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x", - ret); - return NULL; - } - - *size = len; - return data; -} - - -static int write_nfc_pn531(void *data, size_t len) -{ - u32 ret; - - ret = WpsNfcRawWriteToken(data, len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x", - ret); - return -1; - } - - return 0; -} - - -static void deinit_nfc_pn531(void) -{ - u32 ret; - - ret = WpsNfcCloseDevice(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close " - "NFC Device: 0x%08x", ret); - - ret = WpsNfcDeinit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize " - "NFC Library: 0x%08x", ret); -} - - -struct oob_nfc_device_data oob_nfc_pn531_device_data = { - .init_func = init_nfc_pn531, - .read_func = read_nfc_pn531, - .write_func = write_nfc_pn531, - .deinit_func = deinit_nfc_pn531, -}; diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c index 81ddf3a..b650a3c 100644 --- a/contrib/wpa/src/wps/wps_registrar.c +++ b/contrib/wpa/src/wps/wps_registrar.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,13 +15,63 @@ #include "utils/list.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "wps_i.h" #include "wps_dev_attr.h" #include "wps_upnp.h" #include "wps_upnp_i.h" +#ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS +#endif /* CONFIG_WPS_STRICT */ + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_pw_token { + struct dl_list list; + u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + u16 pw_id; + u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t dev_pw_len; +}; + + +static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token) +{ + dl_list_del(&token->list); + os_free(token); +} + + +static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id) +{ + struct wps_nfc_pw_token *token, *prev; + dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token, + list) { + if (pw_id == 0 || pw_id == token->pw_id) + wps_remove_nfc_pw_token(token); + } +} + + +static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens, + u16 pw_id) +{ + struct wps_nfc_pw_token *token; + dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) { + if (pw_id == token->pw_id) + return token; + } + return NULL; +} + +#else /* CONFIG_WPS_NFC */ + +#define wps_free_nfc_pw_tokens(t, p) do { } while (0) + +#endif /* CONFIG_WPS_NFC */ + struct wps_uuid_pin { struct dl_list list; @@ -39,6 +83,7 @@ struct wps_uuid_pin { #define PIN_EXPIRES BIT(1) int flags; struct os_time expiration; + u8 enrollee_addr[ETH_ALEN]; }; @@ -104,7 +149,8 @@ struct wps_registrar { void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, @@ -114,6 +160,7 @@ struct wps_registrar { void *cb_ctx; struct dl_list pins; + struct dl_list nfc_pw_tokens; struct wps_pbc_session *pbc_sessions; int skip_cred_build; @@ -123,10 +170,19 @@ struct wps_registrar { int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; int static_wep_only; + int dualband; struct wps_registrar_device *devices; int force_pbc_overlap; + + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; + u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; + + u8 p2p_dev_addr[ETH_ALEN]; + + u8 pbc_ignore_uuid[WPS_UUID_LEN]; + struct os_time pbc_ignore_start; }; @@ -134,6 +190,54 @@ static int wps_set_ie(struct wps_registrar *reg); static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_registrar_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin); + + +static void wps_registrar_add_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR, + MAC2STR(addr)); + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was " + "already in the list"); + return; /* already in list */ + } + for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1], + ETH_ALEN); + os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", + (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); +} + + +static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR, + MAC2STR(addr)); + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) { + if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0) + break; + } + if (i == WPS_MAX_AUTHORIZED_MACS) { + wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the " + "list"); + return; /* not in the list */ + } + for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1], + ETH_ALEN); + os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0, + ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs", + (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs)); +} static void wps_free_devices(struct wps_registrar_device *dev) @@ -254,20 +358,29 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg, static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, - const u8 *addr, const u8 *uuid_e) + const u8 *uuid_e, + const u8 *p2p_dev_addr) { - struct wps_pbc_session *pbc, *prev = NULL; + struct wps_pbc_session *pbc, *prev = NULL, *tmp; pbc = reg->pbc_sessions; while (pbc) { - if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 && - os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) { + if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 || + (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) && + os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == + 0)) { if (prev) prev->next = pbc->next; else reg->pbc_sessions = pbc->next; - os_free(pbc); - break; + tmp = pbc; + pbc = pbc->next; + wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for " + "addr=" MACSTR, MAC2STR(tmp->addr)); + wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E", + tmp->uuid_e, WPS_UUID_LEN); + os_free(tmp); + continue; } prev = pbc; pbc = pbc->next; @@ -275,26 +388,50 @@ static void wps_registrar_remove_pbc_session(struct wps_registrar *reg, } -static int wps_registrar_pbc_overlap(struct wps_registrar *reg, - const u8 *addr, const u8 *uuid_e) +int wps_registrar_pbc_overlap(struct wps_registrar *reg, + const u8 *addr, const u8 *uuid_e) { int count = 0; struct wps_pbc_session *pbc; + struct wps_pbc_session *first = NULL; struct os_time now; os_get_time(&now); + wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap"); + + if (uuid_e) { + wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID"); + wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID", + uuid_e, WPS_UUID_LEN); + count++; + } + for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) { - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) + wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR, + MAC2STR(pbc->addr)); + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", + pbc->uuid_e, WPS_UUID_LEN); + if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { + wpa_printf(MSG_DEBUG, "WPS: PBC walk time has " + "expired"); break; - if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) || - uuid_e == NULL || - os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) + } + if (first && + os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Same Enrollee"); + continue; /* same Enrollee */ + } + if (uuid_e == NULL || + os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) { + wpa_printf(MSG_DEBUG, "WPS: New Enrollee"); count++; + } + if (first == NULL) + first = pbc; } - if (addr || uuid_e) - count++; + wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count); return count > 1 ? 1 : 0; } @@ -339,7 +476,7 @@ static void wps_registrar_free_pending_m2(struct wps_context *wps) static int wps_build_ap_setup_locked(struct wps_context *wps, struct wpabuf *msg) { - if (wps->ap_setup_locked) { + if (wps->ap_setup_locked && wps->ap_setup_locked != 2) { wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked"); wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED); wpabuf_put_be16(msg, 1); @@ -378,15 +515,59 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, } +static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg, + struct wpabuf *msg) +{ + u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; + if (!reg->sel_reg_union) + return 0; + if (reg->sel_reg_dev_password_id_override >= 0) + id = reg->sel_reg_dev_password_id_override; + if (id != DEV_PW_PUSHBUTTON || !reg->dualband) + return 0; + return wps_build_uuid_e(msg, reg->wps->uuid); +} + + +static void wps_set_pushbutton(u16 *methods, u16 conf_methods) +{ + *methods |= WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == + WPS_CONFIG_VIRT_PUSHBUTTON) + *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == + WPS_CONFIG_PHY_PUSHBUTTON) + *methods |= WPS_CONFIG_PHY_PUSHBUTTON; + if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) != + WPS_CONFIG_VIRT_PUSHBUTTON && + (*methods & WPS_CONFIG_PHY_PUSHBUTTON) != + WPS_CONFIG_PHY_PUSHBUTTON) { + /* + * Required to include virtual/physical flag, but we were not + * configured with push button type, so have to default to one + * of them. + */ + *methods |= WPS_CONFIG_PHY_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ +} + + static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; if (!reg->sel_reg_union) return 0; - methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + methods = reg->wps->config_methods; + methods &= ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); if (reg->sel_reg_config_methods_override >= 0) methods = reg->sel_reg_config_methods_override; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)", @@ -407,6 +588,10 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, * external Registrars. */ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); @@ -418,11 +603,23 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, static int wps_build_config_methods_r(struct wps_registrar *reg, struct wpabuf *msg) { - u16 methods; - methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; - if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; - return wps_build_config_methods(msg, methods); + return wps_build_config_methods(msg, reg->wps->config_methods); +} + + +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count) +{ + *count = 0; + +#ifdef CONFIG_WPS2 + while (*count < WPS_MAX_AUTHORIZED_MACS) { + if (is_zero_ether_addr(reg->authorized_macs_union[*count])) + break; + (*count)++; + } +#endif /* CONFIG_WPS2 */ + + return (const u8 *) reg->authorized_macs_union; } @@ -447,6 +644,7 @@ wps_registrar_init(struct wps_context *wps, return NULL; dl_list_init(®->pins); + dl_list_init(®->nfc_pw_tokens); reg->wps = wps; reg->new_psk_cb = cfg->new_psk_cb; reg->set_ie_cb = cfg->set_ie_cb; @@ -468,6 +666,7 @@ wps_registrar_init(struct wps_context *wps, reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; reg->static_wep_only = cfg->static_wep_only; + reg->dualband = cfg->dualband; if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -489,6 +688,7 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); wps_free_pbc_sessions(reg->pbc_sessions); wpabuf_free(reg->extra_cred); wps_free_devices(reg->devices); @@ -496,23 +696,42 @@ void wps_registrar_deinit(struct wps_registrar *reg) } +static void wps_registrar_invalidate_unused(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin; + + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { + if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) { + wpa_printf(MSG_DEBUG, "WPS: Invalidate previously " + "configured wildcard PIN"); + wps_registrar_remove_pin(reg, pin); + break; + } + } +} + + /** * wps_registrar_add_pin - Configure a new PIN for Registrar * @reg: Registrar data from wps_registrar_init() + * @addr: Enrollee MAC address or %NULL if not known * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ -int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len, int timeout) +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, const u8 *pin, size_t pin_len, + int timeout) { struct wps_uuid_pin *p; p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; + if (addr) + os_memcpy(p->enrollee_addr, addr, ETH_ALEN); if (uuid == NULL) p->wildcard_uuid = 1; else @@ -531,6 +750,9 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, p->expiration.sec += timeout; } + if (p->wildcard_uuid) + wps_registrar_invalidate_unused(reg); + dl_list_add(®->pins, &p->list); wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", @@ -539,6 +761,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; + if (addr) + wps_registrar_add_authorized_mac(reg, addr); + else + wps_registrar_add_authorized_mac( + reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, @@ -549,6 +776,22 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, } +static void wps_registrar_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin) +{ + u8 *addr; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (is_zero_ether_addr(pin->enrollee_addr)) + addr = bcast; + else + addr = pin->enrollee_addr; + wps_registrar_remove_authorized_mac(reg, addr); + wps_remove_pin(pin); + wps_registrar_selected_registrar_changed(reg); +} + + static void wps_registrar_expire_pins(struct wps_registrar *reg) { struct wps_uuid_pin *pin, *prev; @@ -561,9 +804,40 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg) os_time_before(&pin->expiration, &now)) { wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", pin->uuid, WPS_UUID_LEN); - wps_remove_pin(pin); + wps_registrar_remove_pin(reg, pin); + } + } +} + + +/** + * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN + * @reg: Registrar data from wps_registrar_init() + * @dev_pw: PIN to search for or %NULL to match any + * @dev_pw_len: Length of dev_pw in octets + * Returns: 0 on success, -1 if not wildcard PIN is enabled + */ +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, + const u8 *dev_pw, + size_t dev_pw_len) +{ + struct wps_uuid_pin *pin, *prev; + + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { + if (dev_pw && pin->pin && + (dev_pw_len != pin->pin_len || + os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0)) + continue; /* different PIN */ + if (pin->wildcard_uuid) { + wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", + pin->uuid, WPS_UUID_LEN); + wps_registrar_remove_pin(reg, pin); + return 0; } } + + return -1; } @@ -582,7 +856,7 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); - wps_remove_pin(pin); + wps_registrar_remove_pin(reg, pin); return 0; } } @@ -610,10 +884,11 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, /* Check for wildcard UUIDs since none of the UUID-specific * PINs matched */ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { - if (pin->wildcard_uuid == 1) { + if (pin->wildcard_uuid == 1 || + pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); - pin->wildcard_uuid = 2; + pin->wildcard_uuid++; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; @@ -655,7 +930,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { - if (pin->wildcard_uuid == 2) { + if (pin->wildcard_uuid == 3) { wpa_printf(MSG_DEBUG, "WPS: Invalidating used " "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); @@ -673,6 +948,9 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg) { reg->selected_registrar = 0; reg->pbc = 0; + os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); } @@ -690,26 +968,40 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) /** * wps_registrar_button_pushed - Notify Registrar that AP button was pushed * @reg: Registrar data from wps_registrar_init() - * Returns: 0 on success, -1 on failure + * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL + * indicates no such filtering + * Returns: 0 on success, -1 on failure, -2 on session overlap * * This function is called on an AP when a push button is pushed to activate * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout - * or when a PBC registration is completed. + * or when a PBC registration is completed. If more than one Enrollee in active + * PBC mode has been detected during the monitor time (previous 2 minutes), the + * PBC mode is not activated and -2 is returned to indicate session overlap. + * This is skipped if a specific Enrollee is selected. */ -int wps_registrar_button_pushed(struct wps_registrar *reg) +int wps_registrar_button_pushed(struct wps_registrar *reg, + const u8 *p2p_dev_addr) { - if (wps_registrar_pbc_overlap(reg, NULL, NULL)) { + if (p2p_dev_addr == NULL && + wps_registrar_pbc_overlap(reg, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " "mode"); wps_pbc_overlap_event(reg->wps); - return -1; + return -2; } wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; + if (p2p_dev_addr) + os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); + else + os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); wps_registrar_selected_registrar_changed(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, reg, NULL); @@ -734,6 +1026,46 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) } +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len) +{ + if (registrar->pbc) { + wps_registrar_remove_pbc_session(registrar, + uuid_e, NULL); + wps_registrar_pbc_completed(registrar); + os_get_time(®istrar->pbc_ignore_start); + os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); + } else { + wps_registrar_pin_completed(registrar); + } + + if (dev_pw && + wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, + dev_pw_len) == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", + dev_pw, dev_pw_len); + } +} + + +int wps_registrar_wps_cancel(struct wps_registrar *reg) +{ + if (reg->pbc) { + wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it"); + wps_registrar_pbc_timeout(reg, NULL); + eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); + return 1; + } else if (reg->selected_registrar) { + /* PIN Method */ + wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it"); + wps_registrar_pin_completed(reg); + wps_registrar_invalidate_wildcard_pin(reg, NULL, 0); + return 1; + } + return 0; +} + + /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request * @reg: Registrar data from wps_registrar_init() @@ -745,9 +1077,11 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) * situation with other WPS APs. */ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, - const struct wpabuf *wps_data) + const struct wpabuf *wps_data, + int p2p_wildcard) { struct wps_parse_attr attr; + int skip_add = 0; wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Probe Request with WPS data received", @@ -755,11 +1089,6 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, if (wps_parse_msg(wps_data, &attr) < 0) return; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE " - "version 0x%x", attr.version ? *attr.version : 0); - return; - } if (attr.config_methods == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in " @@ -774,7 +1103,7 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, } if (reg->enrollee_seen_cb && attr.uuid_e && - attr.primary_dev_type && attr.request_type) { + attr.primary_dev_type && attr.request_type && !p2p_wildcard) { char *dev_name = NULL; if (attr.dev_name) { dev_name = os_zalloc(attr.dev_name_len + 1); @@ -801,8 +1130,27 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, "UUID-E included"); return; } + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e, + WPS_UUID_LEN); - wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); +#ifdef WPS_WORKAROUNDS + if (reg->pbc_ignore_start.sec && + os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { + struct os_time now, dur; + os_get_time(&now); + os_time_sub(&now, ®->pbc_ignore_start, &dur); + if (dur.sec >= 0 && dur.sec < 5) { + wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " + "based on Probe Request from the Enrollee " + "that just completed PBC provisioning"); + skip_add = 1; + } else + reg->pbc_ignore_start.sec = 0; + } +#endif /* WPS_WORKAROUNDS */ + + if (!skip_add) + wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); reg->force_pbc_overlap = 1; @@ -832,12 +1180,13 @@ static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e, static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, - const u8 *uuid_e) + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) { if (reg->reg_success_cb == NULL) return; - reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e); + reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len); } @@ -856,74 +1205,84 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg) if (reg->selected_registrar) { methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); } + wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d " + "config_methods=0x%x pbc=%d methods=0x%x", + reg->selected_registrar, reg->wps->config_methods, + reg->pbc, methods); + reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar, reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, methods); } -/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ -static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) -{ - struct wpabuf *ie; - const u8 *pos, *end; - - ie = wpabuf_alloc(wpabuf_len(data) + 100); - if (ie == NULL) { - wpabuf_free(data); - return NULL; - } - - pos = wpabuf_head(data); - end = pos + wpabuf_len(data); - - while (end > pos) { - size_t frag_len = end - pos; - if (frag_len > 251) - frag_len = 251; - wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(ie, 4 + frag_len); - wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); - wpabuf_put_data(ie, pos, frag_len); - pos += frag_len; - } - - wpabuf_free(data); - - return ie; -} - - static int wps_set_ie(struct wps_registrar *reg) { struct wpabuf *beacon; struct wpabuf *probe; + const u8 *auth_macs; + size_t count; + size_t vendor_len = 0; + int i; if (reg->set_ie_cb == NULL) return 0; - wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (reg->wps->dev.vendor_ext[i]) { + vendor_len += 2 + 2; + vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]); + } + } - beacon = wpabuf_alloc(300); + beacon = wpabuf_alloc(400 + vendor_len); if (beacon == NULL) return -1; - probe = wpabuf_alloc(400); + probe = wpabuf_alloc(500 + vendor_len); if (probe == NULL) { wpabuf_free(beacon); return -1; } + auth_macs = wps_authorized_macs(reg, &count); + + wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs"); + if (wps_build_version(beacon) || wps_build_wps_state(reg->wps, beacon) || wps_build_ap_setup_locked(reg->wps, beacon) || wps_build_selected_registrar(reg, beacon) || wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || - wps_build_version(probe) || + wps_build_sel_pbc_reg_uuid_e(reg, beacon) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) || + wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_vendor_ext(®->wps->dev, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + +#ifdef CONFIG_P2P + if (wps_build_dev_name(®->wps->dev, beacon) || + wps_build_primary_dev_type(®->wps->dev, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } +#endif /* CONFIG_P2P */ + + wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs"); + + if (wps_build_version(probe) || wps_build_wps_state(reg->wps, probe) || wps_build_ap_setup_locked(reg->wps, probe) || wps_build_selected_registrar(reg, probe) || @@ -934,7 +1293,9 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || - wps_build_rf_bands(®->wps->dev, probe)) { + (reg->dualband && wps_build_rf_bands(®->wps->dev, probe)) || + wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); wpabuf_free(probe); return -1; @@ -987,6 +1348,13 @@ static int wps_get_dev_password(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); pin = (const u8 *) "00000000"; pin_len = 8; +#ifdef CONFIG_WPS_NFC + } else if (wps->nfc_pw_token) { + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC " + "Password Token"); + pin = wps->nfc_pw_token->dev_pw; + pin_len = wps->nfc_pw_token->dev_pw_len; +#endif /* CONFIG_WPS_NFC */ } else { pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, &pin_len); @@ -1025,7 +1393,7 @@ static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg) const u8 *addr[4]; size_t len[4]; - if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) + if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0) return -1; wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPS: R-S2", @@ -1091,7 +1459,7 @@ static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_network_idx(struct wpabuf *msg, const struct wps_credential *cred) { - wpa_printf(MSG_DEBUG, "WPS: * Network Index"); + wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)"); wpabuf_put_be16(msg, ATTR_NETWORK_INDEX); wpabuf_put_be16(msg, 1); wpabuf_put_u8(msg, 1); @@ -1103,6 +1471,8 @@ static int wps_build_cred_ssid(struct wpabuf *msg, const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential", + cred->ssid, cred->ssid_len); wpabuf_put_be16(msg, ATTR_SSID); wpabuf_put_be16(msg, cred->ssid_len); wpabuf_put_data(msg, cred->ssid, cred->ssid_len); @@ -1139,6 +1509,8 @@ static int wps_build_cred_network_key(struct wpabuf *msg, { wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)", (int) cred->key_len); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); wpabuf_put_be16(msg, ATTR_NETWORK_KEY); wpabuf_put_be16(msg, cred->key_len); wpabuf_put_data(msg, cred->key, cred->key_len); @@ -1172,6 +1544,25 @@ static int wps_build_credential(struct wpabuf *msg, } +int wps_build_credential_wrap(struct wpabuf *msg, + const struct wps_credential *cred) +{ + struct wpabuf *wbuf; + wbuf = wpabuf_alloc(200); + if (wbuf == NULL) + return -1; + if (wps_build_credential(wbuf, cred)) { + wpabuf_free(wbuf); + return -1; + } + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(wbuf)); + wpabuf_put_buf(msg, wbuf); + wpabuf_free(wbuf); + return 0; +} + + int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; @@ -1237,7 +1628,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) !wps->wps->registrar->disable_auto_conf) { u8 r[16]; /* Generate a random passphrase */ - if (os_get_random(r, sizeof(r)) < 0) + if (random_get_bytes(r, sizeof(r)) < 0) return -1; os_free(wps->new_psk); wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len); @@ -1269,7 +1660,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk = os_malloc(wps->new_psk_len); if (wps->new_psk == NULL) return -1; - if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) { + if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { os_free(wps->new_psk); wps->new_psk = NULL; return -1; @@ -1283,6 +1674,33 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } use_provided: +#ifdef CONFIG_WPS_TESTING + if (wps_testing_dummy_cred) + cred = wpabuf_alloc(200); + else + cred = NULL; + if (cred) { + struct wps_credential dummy; + wpa_printf(MSG_DEBUG, "WPS: Add dummy credential"); + os_memset(&dummy, 0, sizeof(dummy)); + os_memcpy(dummy.ssid, "dummy", 5); + dummy.ssid_len = 5; + dummy.auth_type = WPS_AUTH_WPA2PSK; + dummy.encr_type = WPS_ENCR_AES; + os_memcpy(dummy.key, "dummy psk", 9); + dummy.key_len = 9; + os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN); + wps_build_credential(cred, &dummy); + wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred); + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(cred)); + wpabuf_put_buf(msg, cred); + + wpabuf_free(cred); + } +#endif /* CONFIG_WPS_TESTING */ + cred = wpabuf_alloc(200); if (cred == NULL) return -1; @@ -1318,11 +1736,40 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg) } +static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + plain = wpabuf_alloc(200); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (wps_build_ap_settings(wps, plain)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(plain)); + wpabuf_put_buf(msg, plain); + wpabuf_free(plain); + + return msg; +} + + static struct wpabuf * wps_build_m2(struct wps_data *wps) { struct wpabuf *msg; - if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0) + if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0) return NULL; wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce", wps->nonce_r, WPS_NONCE_LEN); @@ -1350,6 +1797,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -1388,7 +1836,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_rf_bands(&wps->wps->dev, msg) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || - wps_build_os_version(&wps->wps->dev, msg)) { + wps_build_os_version(&wps->wps->dev, msg) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { wpabuf_free(msg); return NULL; } @@ -1423,6 +1872,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1457,6 +1907,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1493,6 +1944,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || + wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(plain); wpabuf_free(msg); @@ -1505,51 +1957,6 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) } -static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_ACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - -static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) -{ - struct wpabuf *msg; - - wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK"); - - msg = wpabuf_alloc(1000); - if (msg == NULL) - return NULL; - - if (wps_build_version(msg) || - wps_build_msg_type(msg, WPS_WSC_NACK) || - wps_build_enrollee_nonce(wps, msg) || - wps_build_registrar_nonce(wps, msg) || - wps_build_config_error(msg, wps->config_error)) { - wpabuf_free(msg); - return NULL; - } - - return msg; -} - - struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { @@ -1814,6 +2221,13 @@ static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2) wps->wps_pin_revealed = 0; wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e); + /* + * In case wildcard PIN is used and WPS handshake succeeds in the first + * attempt, wps_registrar_unlock_pin() would not free the PIN, so make + * sure the PIN gets invalidated here. + */ + wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); + return 0; } @@ -1842,22 +2256,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->wps->oob_conf.pubkey_hash != NULL) { - const u8 *addr[1]; - u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), - WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); - return -1; - } - } -#endif /* CONFIG_WPS_OOB */ - wpabuf_free(wps->dh_pubkey_e); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_e == NULL) @@ -2047,6 +2445,45 @@ static int wps_process_config_error(struct wps_data *wps, const u8 *err) } +static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps) +{ +#ifdef CONFIG_P2P + struct wps_registrar *reg = wps->wps->registrar; + + if (is_zero_ether_addr(reg->p2p_dev_addr)) + return 1; /* no filtering in use */ + + if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address " + "filtering for PBC: expected " MACSTR " was " + MACSTR " - indicate PBC session overlap", + MAC2STR(reg->p2p_dev_addr), + MAC2STR(wps->p2p_dev_addr)); + return 0; + } +#endif /* CONFIG_P2P */ + return 1; +} + + +static int wps_registrar_skip_overlap(struct wps_data *wps) +{ +#ifdef CONFIG_P2P + struct wps_registrar *reg = wps->wps->registrar; + + if (is_zero_ether_addr(reg->p2p_dev_addr)) + return 0; /* no specific Enrollee selected */ + + if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected " + "Enrollee match"); + return 1; + } +#endif /* CONFIG_P2P */ + return 0; +} + + static enum wps_process_res wps_process_m1(struct wps_data *wps, struct wps_parse_attr *attr) { @@ -2088,25 +2525,46 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, return WPS_CONTINUE; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id >= 0x10 && - wps->dev_pw_id != wps->wps->oob_dev_pw_id) { - wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID " - "%d mismatch", wps->dev_pw_id); - wps->state = SEND_M2D; - return WPS_CONTINUE; +#ifdef CONFIG_WPS_NFC + if (wps->dev_pw_id >= 0x10) { + struct wps_nfc_pw_token *token; + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + token = wps_get_nfc_pw_token( + &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); + if (token) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token"); + dl_list_del(&token->list); + wps->nfc_pw_token = token; + + addr[0] = attr->public_key; + sha256_vector(1, addr, &attr->public_key_len, hash); + if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash " + "mismatch"); + return WPS_FAILURE; + } + } } -#endif /* CONFIG_WPS_OOB */ +#endif /* CONFIG_WPS_NFC */ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { - if (wps->wps->registrar->force_pbc_overlap || - wps_registrar_pbc_overlap(wps->wps->registrar, - wps->mac_addr_e, wps->uuid_e)) { + if ((wps->wps->registrar->force_pbc_overlap || + wps_registrar_pbc_overlap(wps->wps->registrar, + wps->mac_addr_e, wps->uuid_e) || + !wps_registrar_p2p_dev_addr_match(wps)) && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " "negotiation"); wps->state = SEND_M2D; wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; wps_pbc_overlap_event(wps->wps); + wps_fail_event(wps->wps, WPS_M1, + WPS_CFG_MULTIPLE_PBC_DETECTED, + WPS_EI_NO_ERROR); wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } @@ -2150,7 +2608,8 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2187,7 +2646,8 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2210,6 +2670,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -2264,6 +2730,8 @@ static void wps_cred_update(struct wps_credential *dst, static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { + struct wpabuf *msg; + if (wps->wps->ap || wps->er) return 0; @@ -2283,12 +2751,31 @@ static int wps_process_ap_settings_r(struct wps_data *wps, * Use the AP PIN only to receive the current AP settings, not * to reconfigure the AP. */ + + /* + * Clear selected registrar here since we do not get to + * WSC_Done in this protocol run. + */ + wps_registrar_pin_completed(wps->wps->registrar); + + msg = wps_build_ap_cred(wps); + if (msg == NULL) + return -1; + wps->cred.cred_attr = wpabuf_head(msg); + wps->cred.cred_attr_len = wpabuf_len(msg); + if (wps->ap_settings_cb) { wps->ap_settings_cb(wps->ap_settings_cb_ctx, &wps->cred); + wpabuf_free(msg); return 1; } wps_sta_cred_cb(wps); + + wps->cred.cred_attr = NULL; + wps->cred.cred_attr_len = 0; + wpabuf_free(msg); + return 1; } } @@ -2310,7 +2797,8 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } - if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + if (wps->pbc && wps->wps->registrar->force_pbc_overlap && + !wps_registrar_skip_overlap(wps)) { wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " "session overlap"); wps->state = SEND_WSC_NACK; @@ -2333,6 +2821,13 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er, + attr->version2 != NULL) < 0) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings " "attribute"); if (wps_parse_msg(decrypted, &eattr) < 0 || @@ -2362,27 +2857,24 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); - return WPS_FAILURE; + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; } if (*attr.msg_type != WPS_M1 && (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, - WPS_NONCE_LEN != 0))) { + WPS_NONCE_LEN) != 0)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } switch (*attr.msg_type) { case WPS_M1: + if (wps_validate_m1(msg) < 0) + return WPS_FAILURE; #ifdef CONFIG_WPS_UPNP if (wps->wps->wps_upnp && attr.mac_addr) { /* Remove old pending messages when starting new run */ @@ -2397,19 +2889,28 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, ret = wps_process_m1(wps, &attr); break; case WPS_M3: + if (wps_validate_m3(msg) < 0) + return WPS_FAILURE; ret = wps_process_m3(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M3); + wps_fail_event(wps->wps, WPS_M3, wps->config_error, + wps->error_indication); break; case WPS_M5: + if (wps_validate_m5(msg) < 0) + return WPS_FAILURE; ret = wps_process_m5(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M5); + wps_fail_event(wps->wps, WPS_M5, wps->config_error, + wps->error_indication); break; case WPS_M7: + if (wps_validate_m7(msg) < 0) + return WPS_FAILURE; ret = wps_process_m7(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) - wps_fail_event(wps->wps, WPS_M7); + wps_fail_event(wps->wps, WPS_M7, wps->config_error, + wps->error_indication); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -2438,12 +2939,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2467,14 +2962,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2506,6 +3001,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, { struct wps_parse_attr attr; int old_state; + u16 config_error; wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK"); @@ -2515,12 +3011,6 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2541,14 +3031,14 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2559,21 +3049,26 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, return WPS_FAILURE; } + config_error = WPA_GET_BE16(attr.config_error); wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with " - "Configuration Error %d", WPA_GET_BE16(attr.config_error)); + "Configuration Error %d", config_error); switch (old_state) { case RECV_M3: - wps_fail_event(wps->wps, WPS_M2); + wps_fail_event(wps->wps, WPS_M2, config_error, + wps->error_indication); break; case RECV_M5: - wps_fail_event(wps->wps, WPS_M4); + wps_fail_event(wps->wps, WPS_M4, config_error, + wps->error_indication); break; case RECV_M7: - wps_fail_event(wps->wps, WPS_M6); + wps_fail_event(wps->wps, WPS_M6, config_error, + wps->error_indication); break; case RECV_DONE: - wps_fail_event(wps->wps, WPS_M8); + wps_fail_event(wps->wps, WPS_M8, config_error, + wps->error_indication); break; default: break; @@ -2600,12 +3095,6 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, if (wps_parse_msg(msg, &attr) < 0) return WPS_FAILURE; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x", - attr.version ? *attr.version : 0); - return WPS_FAILURE; - } - if (attr.msg_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute"); return WPS_FAILURE; @@ -2628,14 +3117,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2683,15 +3172,22 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } - wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); + wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e, + wps->dev_password, wps->dev_password_len); if (wps->pbc) { wps_registrar_remove_pbc_session(wps->wps->registrar, - wps->mac_addr_e, wps->uuid_e); + wps->uuid_e, + wps->p2p_dev_addr); wps_registrar_pbc_completed(wps->wps->registrar); + os_get_time(&wps->wps->registrar->pbc_ignore_start); + os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e, + WPS_UUID_LEN); } else { wps_registrar_pin_completed(wps->wps->registrar); } + /* TODO: maintain AuthorizedMACs somewhere separately for each ER and + * merge them into APs own list.. */ wps_success_event(wps->wps); @@ -2747,14 +3243,22 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, case WSC_MSG: return wps_process_wsc_msg(wps, msg); case WSC_ACK: + if (wps_validate_wsc_ack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_ack(wps, msg); case WSC_NACK: + if (wps_validate_wsc_nack(msg) < 0) + return WPS_FAILURE; return wps_process_wsc_nack(wps, msg); case WSC_Done: + if (wps_validate_wsc_done(msg) < 0) + return WPS_FAILURE; ret = wps_process_wsc_done(wps, msg); if (ret == WPS_FAILURE) { wps->state = SEND_WSC_NACK; - wps_fail_event(wps->wps, WPS_WSC_DONE); + wps_fail_event(wps->wps, WPS_WSC_DONE, + wps->config_error, + wps->error_indication); } return ret; default: @@ -2787,6 +3291,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, static void wps_registrar_sel_reg_add(struct wps_registrar *reg, struct subscription *s) { + int i, j; wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " "config_methods=0x%x)", s->dev_password_id, s->config_methods); @@ -2796,6 +3301,22 @@ static void wps_registrar_sel_reg_add(struct wps_registrar *reg, if (reg->sel_reg_config_methods_override == -1) reg->sel_reg_config_methods_override = 0; reg->sel_reg_config_methods_override |= s->config_methods; + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (is_zero_ether_addr(reg->authorized_macs_union[i])) + break; + for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS; + j++) { + if (is_zero_ether_addr(s->authorized_macs[j])) + break; + wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: " + MACSTR, MAC2STR(s->authorized_macs[j])); + os_memcpy(reg->authorized_macs_union[i], + s->authorized_macs[j], ETH_ALEN); + i++; + } + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union", + (u8 *) reg->authorized_macs_union, + sizeof(reg->authorized_macs_union)); } #endif /* CONFIG_WPS_UPNP */ @@ -2841,17 +3362,27 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) reg->sel_reg_union = reg->selected_registrar; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; + os_memcpy(reg->authorized_macs_union, reg->authorized_macs, + WPS_MAX_AUTHORIZED_MACS * ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)", + (u8 *) reg->authorized_macs_union, + sizeof(reg->authorized_macs_union)); if (reg->selected_registrar) { - reg->sel_reg_config_methods_override = - reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + u16 methods; + + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; +#ifdef CONFIG_WPS2 + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); +#endif /* CONFIG_WPS2 */ if (reg->pbc) { reg->sel_reg_dev_password_id_override = DEV_PW_PUSHBUTTON; - reg->sel_reg_config_methods_override |= - WPS_CONFIG_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); } wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " "(pbc=%d)", reg->pbc); + reg->sel_reg_config_methods_override = methods; } else wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected"); @@ -2898,3 +3429,124 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, return len; } + + +int wps_registrar_config_ap(struct wps_registrar *reg, + struct wps_credential *cred) +{ +#ifdef CONFIG_WPS2 + printf("encr_type=0x%x\n", cred->encr_type); + if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | + WPS_ENCR_AES))) { + if (cred->encr_type & WPS_ENCR_WEP) { + wpa_printf(MSG_INFO, "WPS: Reject new AP settings " + "due to WEP configuration"); + return -1; + } + + wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to " + "invalid encr_type 0x%x", cred->encr_type); + return -1; + } + + if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == + WPS_ENCR_TKIP) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " + "TKIP+AES"); + cred->encr_type |= WPS_ENCR_AES; + } + + if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) == + WPS_AUTH_WPAPSK) { + wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> " + "WPAPSK+WPA2PSK"); + cred->auth_type |= WPS_AUTH_WPA2PSK; + } +#endif /* CONFIG_WPS2 */ + + if (reg->wps->cred_cb) + return reg->wps->cred_cb(reg->wps->cb_ctx, cred); + + return -1; +} + + +#ifdef CONFIG_WPS_NFC + +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len) +{ + struct wps_nfc_pw_token *token; + + if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); + + token = os_zalloc(sizeof(*token)); + if (token == NULL) + return -1; + + os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + token->pw_id = pw_id; + os_memcpy(token->dev_pw, dev_pw, dev_pw_len); + token->dev_pw_len = dev_pw_len; + + dl_list_add(®->nfc_pw_tokens, &token->list); + + reg->selected_registrar = 1; + reg->pbc = 0; + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); + + return 0; +} + + +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len) +{ + const u8 *pos, *hash, *dev_pw; + u16 id; + size_t dev_pw_len; + + if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN || + oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + hash = oob_dev_pw; + pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN; + id = WPA_GET_BE16(pos); + dev_pw = pos + 2; + dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw; + + wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u", + id); + + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len); + + return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw, + dev_pw_len); +} + + +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token) +{ + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/contrib/wpa/src/wps/wps_ufd.c b/contrib/wpa/src/wps/wps_ufd.c deleted file mode 100644 index 1a911e1..0000000 --- a/contrib/wpa/src/wps/wps_ufd.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * UFD routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <fcntl.h> -#include <dirent.h> - -#include "wps/wps.h" -#include "wps/wps_i.h" - -#ifdef CONFIG_NATIVE_WINDOWS -#define UFD_DIR1 "%s\\SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "\\WFAWSC" -#define UFD_FILE UFD_DIR2 "\\%s" -#else /* CONFIG_NATIVE_WINDOWS */ -#define UFD_DIR1 "%s/SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "/WFAWSC" -#define UFD_FILE UFD_DIR2 "/%s" -#endif /* CONFIG_NATIVE_WINDOWS */ - - -struct wps_ufd_data { - int ufd_fd; -}; - - -static int dev_pwd_e_file_filter(const struct dirent *entry) -{ - unsigned int prefix; - char ext[5]; - - if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2) - return 0; - if (prefix == 0) - return 0; - if (os_strcasecmp(ext, "WFA") != 0) - return 0; - - return 1; -} - - -static int wps_get_dev_pwd_e_file_name(char *path, char *file_name) -{ - struct dirent **namelist; - int i, file_num; - - file_num = scandir(path, &namelist, &dev_pwd_e_file_filter, - alphasort); - if (file_num < 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)", - errno, strerror(errno)); - return -1; - } - if (file_num == 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found"); - os_free(namelist); - return -1; - } - os_strlcpy(file_name, namelist[0]->d_name, 13); - for (i = 0; i < file_num; i++) - os_free(namelist[i]); - os_free(namelist); - return 0; -} - - -static int get_file_name(struct wps_context *wps, int registrar, - const char *path, char *file_name) -{ - switch (wps->oob_conf.oob_method) { - case OOB_METHOD_CRED: - os_snprintf(file_name, 13, "00000000.WSC"); - break; - case OOB_METHOD_DEV_PWD_E: - if (registrar) { - char temp[128]; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0) - return -1; - } else { - u8 *mac_addr = wps->dev.mac_addr; - - os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA", - mac_addr[2], mac_addr[3], mac_addr[4], - mac_addr[5]); - } - break; - case OOB_METHOD_DEV_PWD_R: - os_snprintf(file_name, 13, "00000000.WFA"); - break; - default: - wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method"); - return -1; - } - return 0; -} - - -static int ufd_mkdir(const char *path) -{ - if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory " - "'%s': %d (%s)", path, errno, strerror(errno)); - return -1; - } - return 0; -} - - -static void * init_ufd(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - int write_f; - char temp[128]; - char *path = oob_dev->device_path; - char filename[13]; - struct wps_ufd_data *data; - int ufd_fd; - - if (path == NULL) - return NULL; - - write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ? - !registrar : registrar; - - if (get_file_name(wps, registrar, path, filename) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name"); - return NULL; - } - - if (write_f) { - os_snprintf(temp, sizeof(temp), UFD_DIR1, path); - if (ufd_mkdir(temp)) - return NULL; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (ufd_mkdir(temp)) - return NULL; - } - - os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename); - if (write_f) - ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - else - ufd_fd = open(temp, O_RDONLY); - if (ufd_fd < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s", - temp, strerror(errno)); - return NULL; - } - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->ufd_fd = ufd_fd; - return data; -} - - -static struct wpabuf * read_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - struct wpabuf *buf; - struct stat s; - size_t file_size; - - if (fstat(data->ufd_fd, &s) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size"); - return NULL; - } - - file_size = s.st_size; - buf = wpabuf_alloc(file_size); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read " - "buffer"); - return NULL; - } - - if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) != - (int) file_size) { - wpabuf_free(buf); - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read"); - return NULL; - } - wpabuf_put(buf, file_size); - return buf; -} - - -static int write_ufd(void *priv, struct wpabuf *buf) -{ - struct wps_ufd_data *data = priv; - - if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write"); - return -1; - } - return 0; -} - - -static void deinit_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - close(data->ufd_fd); - os_free(data); -} - - -struct oob_device_data oob_ufd_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_ufd, - .read_func = read_ufd, - .write_func = write_ufd, - .deinit_func = deinit_ufd, -}; diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c index f99b859..09a46a2 100644 --- a/contrib/wpa/src/wps/wps_upnp.c +++ b/contrib/wpa/src/wps/wps_upnp.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * * See below for more details on licensing and code history. */ @@ -47,7 +47,7 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- The http error code generation is pretty bogus, hopefully noone cares. + * -- The http error code generation is pretty bogus, hopefully no one cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work * as explained above and below. @@ -172,7 +172,7 @@ #include "includes.h" -#include <assert.h> +#include <time.h> #include <net/if.h> #include <netdb.h> #include <sys/ioctl.h> @@ -209,6 +209,12 @@ #define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */ #define MAX_ADDR_PER_SUBSCRIPTION 8 +/* Maximum number of Probe Request events per second */ +#define MAX_EVENTS_PER_SEC 5 + + +static struct upnp_wps_device_sm *shared_upnp_device = NULL; + /* Write the current date/time per RFC */ void format_date(struct wpabuf *buf) @@ -270,7 +276,7 @@ static void uuid_make(u8 uuid[UUID_LEN]) /* subscr_addr_delete -- delete single unlinked subscriber address * (be sure to unlink first if need be) */ -static void subscr_addr_delete(struct subscr_addr *a) +void subscr_addr_delete(struct subscr_addr *a) { /* * Note: do NOT free domain_and_port or path because they point to @@ -293,50 +299,46 @@ static void subscr_addr_free_all(struct subscription *s) /* subscr_addr_add_url -- add address(es) for one url to subscription */ -static void subscr_addr_add_url(struct subscription *s, const char *url) +static void subscr_addr_add_url(struct subscription *s, const char *url, + size_t url_len) { int alloc_len; char *scratch_mem = NULL; char *mem; - char *domain_and_port; + char *host; char *delim; char *path; - char *domain; int port = 80; /* port to send to (default is port 80) */ struct addrinfo hints; struct addrinfo *result = NULL; struct addrinfo *rp; int rerr; - struct subscr_addr *a = NULL; + size_t host_len, path_len; /* url MUST begin with http: */ - if (os_strncasecmp(url, "http://", 7)) + if (url_len < 7 || os_strncasecmp(url, "http://", 7)) goto fail; url += 7; + url_len -= 7; - /* allocate memory for the extra stuff we need */ - alloc_len = (2 * (os_strlen(url) + 1)); - scratch_mem = os_zalloc(alloc_len); + /* Make a copy of the string to allow modification during parsing */ + scratch_mem = os_malloc(url_len + 1); if (scratch_mem == NULL) goto fail; - mem = scratch_mem; - strcpy(mem, url); - domain_and_port = mem; - mem += 1 + os_strlen(mem); - delim = os_strchr(domain_and_port, '/'); + os_memcpy(scratch_mem, url, url_len); + scratch_mem[url_len] = '\0'; + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); + host = scratch_mem; + path = os_strchr(host, '/'); + if (path) + *path++ = '\0'; /* null terminate host */ + + /* Process and remove optional port component */ + delim = os_strchr(host, ':'); if (delim) { - *delim++ = 0; /* null terminate domain and port */ - path = delim; - } else { - path = domain_and_port + os_strlen(domain_and_port); - } - domain = mem; - strcpy(domain, domain_and_port); - delim = strchr(domain, ':'); - if (delim) { - *delim++ = 0; /* null terminate domain */ - if (isdigit(*delim)) - port = atol(delim); + *delim = '\0'; /* null terminate host name for now */ + if (isdigit(delim[1])) + port = atol(delim + 1); } /* @@ -359,14 +361,24 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) hints.ai_flags = 0; #endif hints.ai_protocol = 0; /* Any protocol? */ - rerr = getaddrinfo(domain, NULL /* fill in port ourselves */, + rerr = getaddrinfo(host, NULL /* fill in port ourselves */, &hints, &result); if (rerr) { wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", - rerr, gai_strerror(rerr), domain); + rerr, gai_strerror(rerr), host); goto fail; } + + if (delim) + *delim = ':'; /* Restore port */ + + host_len = os_strlen(host); + path_len = path ? os_strlen(path) : 0; + alloc_len = host_len + 1 + 1 + path_len + 1; + for (rp = result; rp; rp = rp->ai_next) { + struct subscr_addr *a; + /* Limit no. of address to avoid denial of service attack */ if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) { wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " @@ -376,28 +388,26 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) a = os_zalloc(sizeof(*a) + alloc_len); if (a == NULL) - continue; - mem = (void *) (a + 1); + break; + mem = (char *) (a + 1); a->domain_and_port = mem; - strcpy(mem, domain_and_port); - mem += 1 + strlen(mem); + os_memcpy(mem, host, host_len); + mem += host_len + 1; a->path = mem; - if (path[0] != '/') + if (path == NULL || path[0] != '/') *mem++ = '/'; - strcpy(mem, path); - mem += 1 + strlen(mem); + if (path) + os_memcpy(mem, path, path_len); os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); dl_list_add(&s->addr_list, &a->list); - a = NULL; /* don't free it below */ } fail: if (result) freeaddrinfo(result); os_free(scratch_mem); - os_free(a); } @@ -407,7 +417,8 @@ fail: static void subscr_addr_list_create(struct subscription *s, const char *url_list) { - char *end; + const char *end; + wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list); for (;;) { while (*url_list == ' ' || *url_list == '\t') url_list++; @@ -417,9 +428,8 @@ static void subscr_addr_list_create(struct subscription *s, end = os_strchr(url_list, '>'); if (end == NULL) break; - *end++ = 0; - subscr_addr_add_url(s, url_list); - url_list = end; + subscr_addr_add_url(s, url_list, end - url_list); + url_list = end + 1; } } @@ -472,12 +482,38 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; const char *format_tail = "</e:propertyset>\n"; + struct os_time now; if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } + if (os_get_time(&now) == 0) { + if (now.sec != sm->last_event_sec) { + sm->last_event_sec = now.sec; + sm->num_events_in_sec = 1; + } else { + sm->num_events_in_sec++; + /* + * In theory, this should apply to all WLANEvent + * notifications, but EAP messages are of much higher + * priority and Probe Request notifications should not + * be allowed to drop EAP messages, so only throttle + * Probe Request notifications. + */ + if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC && + sm->wlanevent_type == + UPNP_WPS_WLANEVENT_TYPE_PROBE) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle " + "event notifications (%u seen " + "during one second)", + sm->num_events_in_sec); + return; + } + } + } + /* Determine buffer size needed first */ buf_size += os_strlen(format_head); buf_size += 50 + 2 * os_strlen("WLANEvent"); @@ -497,12 +533,8 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { - if (event_add(s, buf)) { - wpa_printf(MSG_INFO, "WPS UPnP: Dropping " - "subscriber due to event backlog"); - dl_list_del(&s->list); - subscription_destroy(s); - } + event_add(s, buf, + sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE); } wpabuf_free(buf); @@ -520,10 +552,13 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) */ void subscription_destroy(struct subscription *s) { + struct upnp_wps_device_interface *iface; wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); subscr_addr_free_all(s); event_delete_all(s); - upnp_er_remove_notification(s); + dl_list_for_each(iface, &s->sm->interfaces, + struct upnp_wps_device_interface, list) + upnp_er_remove_notification(iface->wps->registrar, s); os_free(s); } @@ -578,6 +613,7 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); + wps_build_wfa_ext(msg, 0, NULL, 0); return msg; } @@ -605,6 +641,7 @@ static int subscription_first_event(struct subscription *s) "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; const char *tail = "</e:propertyset>\n"; char txt[10]; + int ret; if (s->sm->wlanevent == NULL) { /* @@ -636,7 +673,7 @@ static int subscription_first_event(struct subscription *s) } buf = wpabuf_alloc(500 + os_strlen(wlan_event)); if (buf == NULL) - return 1; + return -1; wpabuf_put_str(buf, head); wpabuf_put_property(buf, "STAStatus", "1"); @@ -646,9 +683,10 @@ static int subscription_first_event(struct subscription *s) wpabuf_put_property(buf, "WLANEvent", wlan_event); wpabuf_put_str(buf, tail); - if (event_add(s, buf)) { + ret = event_add(s, buf, 0); + if (ret) { wpabuf_free(buf); - return 1; + return ret; } wpabuf_free(buf); @@ -692,6 +730,13 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, s->timeout_time = expire; uuid_make(s->uuid); subscr_addr_list_create(s, callback_urls); + if (dl_list_empty(&s->addr_list)) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in " + "'%s' - drop subscription", callback_urls); + subscription_destroy(s); + return NULL; + } + /* Add to end of list, since it has the highest expiration time */ dl_list_add_tail(&sm->subscriptions, &s->list); /* Queue up immediate event message (our last event) @@ -786,6 +831,7 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, os_free(sm->wlanevent); sm->wlanevent = val; + sm->wlanevent_type = ev_type; upnp_wps_device_send_event(sm); ret = 0; @@ -914,10 +960,13 @@ static void upnp_wps_free_msearchreply(struct dl_list *head) } -static void upnp_wps_free_subscriptions(struct dl_list *head) +static void upnp_wps_free_subscriptions(struct dl_list *head, + struct wps_registrar *reg) { struct subscription *s, *tmp; dl_list_for_each_safe(s, tmp, head, struct subscription, list) { + if (reg && s->reg != reg) + continue; dl_list_del(&s->list); subscription_destroy(s); } @@ -928,7 +977,7 @@ static void upnp_wps_free_subscriptions(struct dl_list *head) * upnp_wps_device_stop - Stop WPS UPnP operations on an interface * @sm: WPS UPnP state machine from upnp_wps_device_init() */ -void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) +static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) { if (!sm || !sm->started) return; @@ -936,7 +985,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); web_listener_stop(sm); upnp_wps_free_msearchreply(&sm->msearch_replies); - upnp_wps_free_subscriptions(&sm->subscriptions); + upnp_wps_free_subscriptions(&sm->subscriptions, NULL); advertisement_state_machine_stop(sm, 1); @@ -960,7 +1009,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) * @net_if: Selected network interface name * Returns: 0 on success, -1 on failure */ -int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) +static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) { if (!sm || !net_if) return -1; @@ -1015,24 +1064,59 @@ fail: } +static struct upnp_wps_device_interface * +upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) +{ + struct upnp_wps_device_interface *iface; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (iface->priv == priv) + return iface; + } + return NULL; +} + + /** * upnp_wps_device_deinit - Deinitialize WPS UPnP * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @priv: External context data that was used in upnp_wps_device_init() call */ -void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) { + struct upnp_wps_device_interface *iface; + if (!sm) return; - upnp_wps_device_stop(sm); - - if (sm->peer.wps) - wps_deinit(sm->peer.wps); - os_free(sm->root_dir); - os_free(sm->desc_url); - os_free(sm->ctx->ap_pin); - os_free(sm->ctx); - os_free(sm); + iface = upnp_wps_get_iface(sm, priv); + if (iface == NULL) { + wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface " + "instance to deinit"); + return; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface); + if (dl_list_len(&sm->interfaces) == 1) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance " + "- free global device instance"); + upnp_wps_device_stop(sm); + } else + upnp_wps_free_subscriptions(&sm->subscriptions, + iface->wps->registrar); + dl_list_del(&iface->list); + + if (iface->peer.wps) + wps_deinit(iface->peer.wps); + os_free(iface->ctx->ap_pin); + os_free(iface->ctx); + os_free(iface); + + if (dl_list_empty(&sm->interfaces)) { + os_free(sm->root_dir); + os_free(sm->desc_url); + os_free(sm); + shared_upnp_device = NULL; + } } @@ -1041,25 +1125,59 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) * @ctx: callback table; we must eventually free it * @wps: Pointer to longterm WPS context * @priv: External context data that will be used in callbacks + * @net_if: Selected network interface name * Returns: WPS UPnP state or %NULL on failure */ struct upnp_wps_device_sm * upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, - void *priv) + void *priv, char *net_if) { struct upnp_wps_device_sm *sm; + struct upnp_wps_device_interface *iface; + int start = 0; - sm = os_zalloc(sizeof(*sm)); - if (!sm) { - wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed"); + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) { + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); + + iface->ctx = ctx; + iface->wps = wps; + iface->priv = priv; + + if (shared_upnp_device) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device " + "context"); + sm = shared_upnp_device; + } else { + wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context"); + sm = os_zalloc(sizeof(*sm)); + if (!sm) { + wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init " + "failed"); + os_free(iface); + os_free(ctx->ap_pin); + os_free(ctx); + return NULL; + } + shared_upnp_device = sm; + + dl_list_init(&sm->msearch_replies); + dl_list_init(&sm->subscriptions); + dl_list_init(&sm->interfaces); + start = 1; + } + + dl_list_add(&sm->interfaces, &iface->list); + + if (start && upnp_wps_device_start(sm, net_if)) { + upnp_wps_device_deinit(sm, priv); return NULL; } - sm->ctx = ctx; - sm->wps = wps; - sm->priv = priv; - dl_list_init(&sm->msearch_replies); - dl_list_init(&sm->subscriptions); return sm; } @@ -1078,16 +1196,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin) { + struct upnp_wps_device_interface *iface; if (sm == NULL) return 0; - os_free(sm->ctx->ap_pin); - if (ap_pin) { - sm->ctx->ap_pin = os_strdup(ap_pin); - if (sm->ctx->ap_pin == NULL) - return -1; - } else - sm->ctx->ap_pin = NULL; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + os_free(iface->ctx->ap_pin); + if (ap_pin) { + iface->ctx->ap_pin = os_strdup(ap_pin); + if (iface->ctx->ap_pin == NULL) + return -1; + } else + iface->ctx->ap_pin = NULL; + } return 0; } diff --git a/contrib/wpa/src/wps/wps_upnp.h b/contrib/wpa/src/wps/wps_upnp.h index 06bc31f..87b7ab1 100644 --- a/contrib/wpa/src/wps/wps_upnp.h +++ b/contrib/wpa/src/wps/wps_upnp.h @@ -35,11 +35,8 @@ struct upnp_wps_device_ctx { struct upnp_wps_device_sm * upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, - void *priv); -void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm); - -int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if); -void upnp_wps_device_stop(struct upnp_wps_device_sm *sm); + void *priv, char *net_if); +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv); int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, const u8 from_mac_addr[ETH_ALEN], diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c index 93746da..54ed98f 100644 --- a/contrib/wpa/src/wps/wps_upnp_ap.c +++ b/contrib/wpa/src/wps/wps_upnp_ap.c @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - UPnP AP functionality * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -25,9 +19,10 @@ static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) { struct subscription *s = eloop_ctx; + struct wps_registrar *reg = timeout_ctx; wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); s->selected_registrar = 0; - wps_registrar_selected_registrar_changed(s->reg); + wps_registrar_selected_registrar_changed(reg); } @@ -39,18 +34,16 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", msg); + if (wps_validate_upnp_set_selected_registrar(msg) < 0) + return -1; if (wps_parse_msg(msg, &attr) < 0) return -1; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " - "version 0x%x", attr.version ? *attr.version : 0); - return -1; - } s->reg = reg; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs)); if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " "Selected Registrar"); @@ -61,8 +54,21 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; s->config_methods = attr.sel_reg_config_methods ? WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + if (attr.authorized_macs) { + int count = attr.authorized_macs_len / ETH_ALEN; + if (count > WPS_MAX_AUTHORIZED_MACS) + count = WPS_MAX_AUTHORIZED_MACS; + os_memcpy(s->authorized_macs, attr.authorized_macs, + count * ETH_ALEN); + } else if (!attr.version2) { +#ifdef CONFIG_WPS2 + wpa_printf(MSG_DEBUG, "WPS: Add broadcast " + "AuthorizedMACs for WPS 1.0 ER"); + os_memset(s->authorized_macs, 0xff, ETH_ALEN); +#endif /* CONFIG_WPS2 */ + } eloop_register_timeout(WPS_PBC_WALK_TIME, 0, - upnp_er_set_selected_timeout, s, NULL); + upnp_er_set_selected_timeout, s, reg); } wps_registrar_selected_registrar_changed(reg); @@ -71,10 +77,11 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, } -void upnp_er_remove_notification(struct subscription *s) +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s) { s->selected_registrar = 0; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); - if (s->reg) - wps_registrar_selected_registrar_changed(s->reg); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + if (reg) + wps_registrar_selected_registrar_changed(reg); } diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c index ae5efdb..2c8ed4f1 100644 --- a/contrib/wpa/src/wps/wps_upnp_event.c +++ b/contrib/wpa/src/wps/wps_upnp_event.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> * * See wps_upnp.c for more details on licensing and code history. */ @@ -31,7 +31,7 @@ */ #define MAX_EVENTS_QUEUED 20 /* How far behind queued events */ -#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */ +#define MAX_FAILURES 10 /* Drop subscription after this many failures */ /* How long to wait before sending event */ #define EVENT_DELAY_SECONDS 0 @@ -73,6 +73,7 @@ static void event_clean(struct wps_event_ *e) */ static void event_delete(struct wps_event_ *e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e); event_clean(e); wpabuf_free(e->data); os_free(e); @@ -86,8 +87,11 @@ static struct wps_event_ *event_dequeue(struct subscription *s) { struct wps_event_ *e; e = dl_list_first(&s->event_queue, struct wps_event_, list); - if (e) + if (e) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for " + "subscription %p", e, s); dl_list_del(&e->list); + } return e; } @@ -115,14 +119,22 @@ static void event_retry(struct wps_event_ *e, int do_next_address) struct subscription *s = e->s; struct upnp_wps_device_sm *sm = s->sm; + wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p", + e, s); event_clean(e); /* will set: s->current_event = NULL; */ - if (do_next_address) + if (do_next_address) { e->retry++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry); + } if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); + event_delete(e); + s->last_event_failed = 1; + if (!dl_list_empty(&s->event_queue)) + event_send_all_later(s->sm); return; } dl_list_add(&s->event_queue, &e->list); @@ -158,17 +170,60 @@ static struct wpabuf * event_build_message(struct wps_event_ *e) } +static void event_addr_failure(struct wps_event_ *e) +{ + struct subscription *s = e->s; + + e->addr->num_failures++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s " + "(num_failures=%u)", + e, e->addr->domain_and_port, e->addr->num_failures); + + if (e->addr->num_failures < MAX_FAILURES) { + /* Try other addresses, if available */ + event_retry(e, 1); + return; + } + + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping them...). + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p " + "address %s due to errors", s, e->addr->domain_and_port); + dl_list_del(&e->addr->list); + subscr_addr_delete(e->addr); + e->addr = NULL; + + if (dl_list_empty(&s->addr_list)) { + /* if we've given up on all addresses */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p " + "with no addresses", s); + dl_list_del(&s->list); + subscription_destroy(s); + return; + } + + /* Try other addresses, if available */ + event_retry(e, 0); +} + + static void event_http_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_event_ *e = ctx; struct subscription *s = e->s; + wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p " + "event=%d", e, c, event); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, - "WPS UPnP: Got event reply OK from " - "%s", e->addr->domain_and_port); + "WPS UPnP: Got event %p reply OK from %s", + e, e->addr->domain_and_port); + e->addr->num_failures = 0; + s->last_event_failed = 0; event_delete(e); /* Schedule sending more if there is more to send */ @@ -176,24 +231,17 @@ static void event_http_cb(void *ctx, struct http_client *c, event_send_all_later(s->sm); break; case HTTP_CLIENT_FAILED: + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure"); + event_addr_failure(e); + break; case HTTP_CLIENT_INVALID_REPLY: - wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s", - e->addr->domain_and_port); - - /* - * If other side doesn't like what we say, forget about them. - * (There is no way to tell other side that we are dropping - * them...). - * Alternately, we could just do event_delete(e) - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to " - "errors"); - dl_list_del(&s->list); - subscription_destroy(s); + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply"); + event_addr_failure(e); break; case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); - event_retry(e, 1); + event_addr_failure(e); + break; } } @@ -228,9 +276,12 @@ static int event_send_start(struct subscription *s) * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - assert(!dl_list_empty(&s->addr_list)); - assert(s->current_event == NULL); - assert(!dl_list_empty(&s->event_queue)); + if (dl_list_empty(&s->addr_list)) + return -1; + if (s->current_event) + return -1; + if (dl_list_empty(&s->event_queue)) + return -1; s->current_event = e = event_dequeue(s); @@ -270,18 +321,10 @@ static void event_send_all_later_handler(void *eloop_data, void *user_ctx) sm->event_send_all_queued = 0; dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, list) { - if (dl_list_empty(&s->addr_list)) { - /* if we've given up on all addresses */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " - "subscription with no addresses"); - dl_list_del(&s->list); - subscription_destroy(s); - } else { - if (s->current_event == NULL /* not busy */ && - !dl_list_empty(&s->event_queue) /* more to do */) { - if (event_send_start(s)) - nerrors++; - } + if (s->current_event == NULL /* not busy */ && + !dl_list_empty(&s->event_queue) /* more to do */) { + if (event_send_start(s)) + nerrors++; } } @@ -326,31 +369,54 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm) * event_add - Add a new event to a queue * @s: Subscription * @data: Event data (is copied; caller retains ownership) - * Returns: 0 on success, 1 on error + * @probereq: Whether this is a Probe Request event + * Returns: 0 on success, -1 on error, 1 on max event queue limit reached */ -int event_add(struct subscription *s, const struct wpabuf *data) +int event_add(struct subscription *s, const struct wpabuf *data, int probereq) { struct wps_event_ *e; + unsigned int len; - if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) { + len = dl_list_len(&s->event_queue); + if (len >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " - "subscriber"); - return 1; + "subscriber %p", s); + if (probereq) + return 1; + + /* Drop oldest entry to allow EAP event to be stored. */ + e = event_dequeue(s); + if (!e) + return 1; + event_delete(e); + } + + if (s->last_event_failed && probereq && len > 0) { + /* + * Avoid queuing frames for subscribers that may have left + * without unsubscribing. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe " + "Request frames for subscription %p since last " + "delivery failed", s); + return -1; } e = os_zalloc(sizeof(*e)); if (e == NULL) - return 1; + return -1; dl_list_init(&e->list); e->s = s; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); - return 1; + return -1; } e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; + wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p " + "(queue len %u)", e, s, len + 1); dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; diff --git a/contrib/wpa/src/wps/wps_upnp_i.h b/contrib/wpa/src/wps/wps_upnp_i.h index b31875a..7f3c561 100644 --- a/contrib/wpa/src/wps/wps_upnp_i.h +++ b/contrib/wpa/src/wps/wps_upnp_i.h @@ -67,6 +67,7 @@ struct subscr_addr { char *domain_and_port; /* domain and port part of url */ char *path; /* "filepath" part of url (from "mem") */ struct sockaddr_in saddr; /* address for doing connect */ + unsigned num_failures; }; @@ -91,25 +92,37 @@ struct subscription { struct dl_list event_queue; /* Queued event messages. */ struct wps_event_ *current_event; /* non-NULL if being sent (not in q) */ + int last_event_failed; /* Whether delivery of last event failed */ /* Information from SetSelectedRegistrar action */ u8 selected_registrar; u16 dev_password_id; u16 config_methods; + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; struct wps_registrar *reg; }; +struct upnp_wps_device_interface { + struct dl_list list; + struct upnp_wps_device_ctx *ctx; /* callback table */ + struct wps_context *wps; + void *priv; + + /* FIX: maintain separate structures for each UPnP peer */ + struct upnp_wps_peer peer; +}; + /* - * Our instance data corresponding to one WiFi network interface - * (multiple might share the same wired network interface!). + * Our instance data corresponding to the AP device. Note that there may be + * multiple wireless interfaces sharing the same UPnP device instance. Each + * such interface is stored in the list of struct upnp_wps_device_interface + * instances. * * This is known as an opaque struct declaration to users of the WPS UPnP code. */ struct upnp_wps_device_sm { - struct upnp_wps_device_ctx *ctx; /* callback table */ - struct wps_context *wps; - void *priv; + struct dl_list interfaces; /* struct upnp_wps_device_interface */ char *root_dir; char *desc_url; int started; /* nonzero if we are active */ @@ -130,9 +143,9 @@ struct upnp_wps_device_sm { */ char *wlanevent; /* the last WLANEvent data */ - - /* FIX: maintain separate structures for each UPnP peer */ - struct upnp_wps_peer peer; + enum upnp_wps_wlanevent_type wlanevent_type; + os_time_t last_event_sec; + unsigned int num_events_in_sec; }; /* wps_upnp.c */ @@ -144,6 +157,7 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, void subscription_destroy(struct subscription *s); struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); +void subscr_addr_delete(struct subscr_addr *a); int send_wpabuf(int fd, struct wpabuf *buf); int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, u8 mac[ETH_ALEN]); @@ -165,7 +179,7 @@ int web_listener_start(struct upnp_wps_device_sm *sm); void web_listener_stop(struct upnp_wps_device_sm *sm); /* wps_upnp_event.c */ -int event_add(struct subscription *s, const struct wpabuf *data); +int event_add(struct subscription *s, const struct wpabuf *data, int probereq); void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); @@ -174,6 +188,7 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm); int upnp_er_set_selected_registrar(struct wps_registrar *reg, struct subscription *s, const struct wpabuf *msg); -void upnp_er_remove_notification(struct subscription *s); +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s); #endif /* WPS_UPNP_I_H */ diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c index 8505d05..17a8207 100644 --- a/contrib/wpa/src/wps/wps_upnp_ssdp.c +++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -97,16 +97,6 @@ static int line_length(const char *l) } -/* No. of chars excluding trailing whitespace */ -static int line_length_stripped(const char *l) -{ - const char *lp = l + line_length(l); - while (lp > l && !isgraph(lp[-1])) - lp--; - return lp - l; -} - - static int str_starts(const char *str, const char *start) { return os_strncmp(str, start, os_strlen(start)) == 0; @@ -136,9 +126,12 @@ next_advertisement(struct upnp_wps_device_sm *sm, struct wpabuf *msg; char *NTString = ""; char uuid_string[80]; + struct upnp_wps_device_interface *iface; *islast = 0; - uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) goto fail; @@ -527,7 +520,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, #ifndef CONFIG_NO_STDOUT_DEBUG const char *start = data; #endif /* CONFIG_NO_STDOUT_DEBUG */ - const char *end; int got_host = 0; int got_st = 0, st_match = 0; int got_man = 0; @@ -542,7 +534,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, /* Parse remaining lines */ for (; *data != '\0'; data += line_length(data)) { - end = data + line_length_stripped(data); if (token_eq(data, "host")) { /* The host line indicates who the packet * is addressed to... but do we really care? @@ -588,8 +579,13 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, } if (str_starts(data, "uuid:")) { char uuid_string[80]; + struct upnp_wps_device_interface *iface; + iface = dl_list_first( + &sm->interfaces, + struct upnp_wps_device_interface, + list); data += os_strlen("uuid:"); - uuid_bin2str(sm->wps->uuid, uuid_string, + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); if (str_starts(data, uuid_string)) st_match = 1; @@ -870,16 +866,26 @@ int ssdp_open_multicast_sock(u32 ip_addr) return -1; #if 0 /* maybe ok if we sometimes block on writes */ - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) { + close(sd); return -1; + } #endif if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, - &ip_addr, sizeof(ip_addr))) + &ip_addr, sizeof(ip_addr))) { + wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: " + "%d (%s)", ip_addr, errno, strerror(errno)); + close(sd); return -1; + } if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, - &ttl, sizeof(ttl))) + &ttl, sizeof(ttl))) { + wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): " + "%d (%s)", errno, strerror(errno)); + close(sd); return -1; + } #if 0 /* not needed, because we don't receive using multicast_sd */ { @@ -896,6 +902,7 @@ int ssdp_open_multicast_sock(u32 ip_addr) "WPS UPnP: setsockopt " "IP_ADD_MEMBERSHIP errno %d (%s)", errno, strerror(errno)); + close(sd); return -1; } } diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c index 9a6b36e..ce0bede 100644 --- a/contrib/wpa/src/wps/wps_upnp_web.c +++ b/contrib/wpa/src/wps/wps_upnp_web.c @@ -184,6 +184,10 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, { const char *s; char uuid_string[80]; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); wpabuf_put_str(buf, wps_device_xml_prefix); @@ -191,38 +195,38 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, * Add required fields with default values if not configured. Add * optional and recommended fields only if configured. */ - s = sm->wps->friendly_name; + s = iface->wps->friendly_name; s = ((s && *s) ? s : "WPS Access Point"); xml_add_tagged_data(buf, "friendlyName", s); - s = sm->wps->dev.manufacturer; + s = iface->wps->dev.manufacturer; s = ((s && *s) ? s : ""); xml_add_tagged_data(buf, "manufacturer", s); - if (sm->wps->manufacturer_url) + if (iface->wps->manufacturer_url) xml_add_tagged_data(buf, "manufacturerURL", - sm->wps->manufacturer_url); + iface->wps->manufacturer_url); - if (sm->wps->model_description) + if (iface->wps->model_description) xml_add_tagged_data(buf, "modelDescription", - sm->wps->model_description); + iface->wps->model_description); - s = sm->wps->dev.model_name; + s = iface->wps->dev.model_name; s = ((s && *s) ? s : ""); xml_add_tagged_data(buf, "modelName", s); - if (sm->wps->dev.model_number) + if (iface->wps->dev.model_number) xml_add_tagged_data(buf, "modelNumber", - sm->wps->dev.model_number); + iface->wps->dev.model_number); - if (sm->wps->model_url) - xml_add_tagged_data(buf, "modelURL", sm->wps->model_url); + if (iface->wps->model_url) + xml_add_tagged_data(buf, "modelURL", iface->wps->model_url); - if (sm->wps->dev.serial_number) + if (iface->wps->dev.serial_number) xml_add_tagged_data(buf, "serialNumber", - sm->wps->dev.serial_number); + iface->wps->dev.serial_number); - uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); s = uuid_string; /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data() * easily... @@ -231,8 +235,8 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, xml_data_encode(buf, s, os_strlen(s)); wpabuf_put_str(buf, "</UDN>\n"); - if (sm->wps->upc) - xml_add_tagged_data(buf, "UPC", sm->wps->upc); + if (iface->wps->upc) + xml_add_tagged_data(buf, "UPC", iface->wps->upc); wpabuf_put_str(buf, wps_device_xml_postfix); } @@ -311,6 +315,10 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, size_t extra_len = 0; int body_length; char len_buf[10]; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); /* * It is not required that filenames be case insensitive but it is @@ -322,16 +330,16 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML"); req = GET_DEVICE_XML_FILE; extra_len = 3000; - if (sm->wps->friendly_name) - extra_len += os_strlen(sm->wps->friendly_name); - if (sm->wps->manufacturer_url) - extra_len += os_strlen(sm->wps->manufacturer_url); - if (sm->wps->model_description) - extra_len += os_strlen(sm->wps->model_description); - if (sm->wps->model_url) - extra_len += os_strlen(sm->wps->model_url); - if (sm->wps->upc) - extra_len += os_strlen(sm->wps->upc); + if (iface->wps->friendly_name) + extra_len += os_strlen(iface->wps->friendly_name); + if (iface->wps->manufacturer_url) + extra_len += os_strlen(iface->wps->manufacturer_url); + if (iface->wps->model_description) + extra_len += os_strlen(iface->wps->model_description); + if (iface->wps->model_url) + extra_len += os_strlen(iface->wps->model_url); + if (iface->wps->upc) + extra_len += os_strlen(iface->wps->upc); } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) { wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML"); req = GET_SCPD_XML_FILE; @@ -408,11 +416,16 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, { static const char *name = "NewDeviceInfo"; struct wps_config cfg; - struct upnp_wps_peer *peer = &sm->peer; + struct upnp_wps_device_interface *iface; + struct upnp_wps_peer *peer; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); + peer = &iface->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (sm->ctx->ap_pin == NULL) + if (iface->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; /* @@ -427,9 +440,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, wps_deinit(peer->wps); os_memset(&cfg, 0, sizeof(cfg)); - cfg.wps = sm->wps; - cfg.pin = (u8 *) sm->ctx->ap_pin; - cfg.pin_len = os_strlen(sm->ctx->ap_pin); + cfg.wps = iface->wps; + cfg.pin = (u8 *) iface->ctx->ap_pin; + cfg.pin_len = os_strlen(iface->ctx->ap_pin); peer->wps = wps_init(&cfg); if (peer->wps) { enum wsc_op_code op_code; @@ -458,6 +471,10 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, enum http_reply_code ret; enum wps_process_res res; enum wsc_op_code op_code; + struct upnp_wps_device_interface *iface; + + iface = dl_list_first(&sm->interfaces, + struct upnp_wps_device_interface, list); /* * PutMessage is used by external UPnP-based Registrar to perform WPS @@ -468,11 +485,11 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg); + res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg); if (res == WPS_FAILURE) *reply = NULL; else - *reply = wps_get_msg(sm->peer.wps, &op_code); + *reply = wps_get_msg(iface->peer.wps, &op_code); wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; @@ -491,6 +508,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, int ev_type; int type; char *val; + struct upnp_wps_device_interface *iface; + int ok = 0; /* * External UPnP-based Registrar is passing us a message to be proxied @@ -523,6 +542,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, if (hwaddr_aton(val, macaddr)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in " "PutWLANResponse: '%s'", val); +#ifdef CONFIG_WPS_STRICT + { + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) < 0 || attr.version2) { + wpabuf_free(msg); + os_free(val); + return UPNP_ARG_VALUE_INVALID; + } + } +#endif /* CONFIG_WPS_STRICT */ if (hwaddr_aton2(val, macaddr) > 0) { /* * At least some versions of Intel PROset seem to be @@ -530,7 +559,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, */ wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow " "incorrect MAC address format in " - "NewWLANEventMAC"); + "NewWLANEventMAC: %s -> " MACSTR, + val, MAC2STR(macaddr)); } else { wpabuf_free(msg); os_free(val); @@ -548,9 +578,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type); } else type = -1; - if (!sm->ctx->rx_req_put_wlan_response || - sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg, - type)) { + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (iface->ctx->rx_req_put_wlan_response && + iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type, + macaddr, msg, type) + == 0) + ok = 1; + } + + if (!ok) { wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->" "rx_req_put_wlan_response"); wpabuf_free(msg); @@ -595,6 +632,8 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, struct wpabuf *msg; enum http_reply_code ret; struct subscription *s; + struct upnp_wps_device_interface *iface; + int err = 0; wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); s = find_er(sm, cli); @@ -606,11 +645,15 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, msg = xml_get_base64_item(data, "NewMessage", &ret); if (msg == NULL) return ret; - if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + dl_list_for_each(iface, &sm->interfaces, + struct upnp_wps_device_interface, list) { + if (upnp_er_set_selected_registrar(iface->wps->registrar, s, + msg)) + err = 1; } wpabuf_free(msg); + if (err) + return HTTP_INTERNAL_SERVER_ERROR; *replyname = NULL; *reply = NULL; return HTTP_OK; @@ -890,6 +933,9 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, return; } + wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE", + (u8 *) hdr, os_strlen(hdr)); + /* Parse/validate headers */ h = hdr; /* First line: SUBSCRIBE /wps_event HTTP/1.1 @@ -910,7 +956,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, break; /* no unterminated lines allowed */ /* NT assures that it is our type of subscription; - * not used for a renewl. + * not used for a renewal. **/ match = "NT:"; match_len = os_strlen(match); @@ -989,16 +1035,22 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, if (got_uuid) { /* renewal */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal"); if (callback_urls) { ret = HTTP_BAD_REQUEST; goto error; } s = subscription_renew(sm, uuid); if (s == NULL) { + char str[80]; + uuid_bin2str(uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find " + "SID %s", str); ret = HTTP_PRECONDITION_FAILED; goto error; } } else if (callback_urls) { + wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription"); if (!got_nt) { ret = HTTP_PRECONDITION_FAILED; goto error; @@ -1022,6 +1074,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, /* subscription id */ b = wpabuf_put(buf, 0); uuid_bin2str(s->uuid, b, 80); + wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b); wpabuf_put(buf, os_strlen(b)); wpabuf_put_str(buf, "\r\n"); wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC); @@ -1055,6 +1108,7 @@ error: * HTTP 500-series error code. * 599 Too many subscriptions (not a standard HTTP error) */ + wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret); http_put_empty(buf, ret); http_request_send_and_deinit(req, buf); os_free(callback_urls); diff --git a/contrib/wpa/src/wps/wps_validate.c b/contrib/wpa/src/wps/wps_validate.c new file mode 100644 index 0000000..e366256 --- /dev/null +++ b/contrib/wpa/src/wps/wps_validate.c @@ -0,0 +1,1975 @@ +/* + * Wi-Fi Protected Setup - Strict protocol validation routines + * Copyright (c) 2010, Atheros Communications, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "wps_i.h" +#include "wps.h" + + +#ifndef WPS_STRICT_ALL +#define WPS_STRICT_WPS2 +#endif /* WPS_STRICT_ALL */ + + +static int wps_validate_version(const u8 *version, int mandatory) +{ + if (version == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute " + "missing"); + return -1; + } + return 0; + } + if (*version != 0x10) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute " + "value 0x%x", *version); + return -1; + } + return 0; +} + + +static int wps_validate_version2(const u8 *version2, int mandatory) +{ + if (version2 == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute " + "missing"); + return -1; + } + return 0; + } + if (*version2 < 0x20) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute " + "value 0x%x", *version2); + return -1; + } + return 0; +} + + +static int wps_validate_request_type(const u8 *request_type, int mandatory) +{ + if (request_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Request Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*request_type > 0x03) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type " + "attribute value 0x%x", *request_type); + return -1; + } + return 0; +} + + +static int wps_validate_response_type(const u8 *response_type, int mandatory) +{ + if (response_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Response Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*response_type > 0x03) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type " + "attribute value 0x%x", *response_type); + return -1; + } + return 0; +} + + +static int valid_config_methods(u16 val, int wps2) +{ + if (wps2) { + if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual " + "Display flag without old Display flag " + "set"); + return 0; + } + if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Display flag " + "without Physical/Virtual Display flag"); + return 0; + } + if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual " + "PushButton flag without old PushButton " + "flag set"); + return 0; + } + if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) { + wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag " + "without Physical/Virtual PushButton flag"); + return 0; + } + } + + return 1; +} + + +static int wps_validate_config_methods(const u8 *config_methods, int wps2, + int mandatory) +{ + u16 val; + + if (config_methods == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Configuration " + "Methods attribute missing"); + return -1; + } + return 0; + } + + val = WPA_GET_BE16(config_methods); + if (!valid_config_methods(val, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration " + "Methods attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2, + int mandatory) +{ + u16 val; + + if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0) + return -1; + if (config_methods == NULL) + return 0; + val = WPA_GET_BE16(config_methods); + if (val & WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration " + "Methods attribute value 0x%04x in AP info " + "(PushButton not allowed for registering new ER)", + val); + return -1; + } + return 0; +} + + +static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory) +{ + if (uuid_e == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory) +{ + if (uuid_r == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_primary_dev_type(const u8 *primary_dev_type, + int mandatory) +{ + if (primary_dev_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory) +{ + if (rf_bands == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands " + "attribute missing"); + return -1; + } + return 0; + } + if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ && + *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands " + "attribute value 0x%x", *rf_bands); + return -1; + } + return 0; +} + + +static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory) +{ + u16 val; + if (assoc_state == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Association State " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(assoc_state); + if (val > 4) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_config_error(const u8 *config_error, int mandatory) +{ + u16 val; + + if (config_error == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(config_error); + if (val > 18) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_dev_password_id(const u8 *dev_password_id, + int mandatory) +{ + u16 val; + + if (dev_password_id == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(dev_password_id); + if (val >= 0x0006 && val <= 0x000f) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_manufacturer(const u8 *manufacturer, size_t len, + int mandatory) +{ + if (manufacturer == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && manufacturer[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer " + "attribute value", manufacturer, len); + return -1; + } + return 0; +} + + +static int wps_validate_model_name(const u8 *model_name, size_t len, + int mandatory) +{ + if (model_name == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Model Name " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && model_name[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name " + "attribute value", model_name, len); + return -1; + } + return 0; +} + + +static int wps_validate_model_number(const u8 *model_number, size_t len, + int mandatory) +{ + if (model_number == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Model Number " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && model_number[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number " + "attribute value", model_number, len); + return -1; + } + return 0; +} + + +static int wps_validate_serial_number(const u8 *serial_number, size_t len, + int mandatory) +{ + if (serial_number == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && serial_number[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial " + "Number attribute value", + serial_number, len); + return -1; + } + return 0; +} + + +static int wps_validate_dev_name(const u8 *dev_name, size_t len, + int mandatory) +{ + if (dev_name == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Device Name " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 0 && dev_name[len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name " + "attribute value", dev_name, len); + return -1; + } + return 0; +} + + +static int wps_validate_request_to_enroll(const u8 *request_to_enroll, + int mandatory) +{ + if (request_to_enroll == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll " + "attribute missing"); + return -1; + } + return 0; + } + if (*request_to_enroll > 0x01) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll " + "attribute value 0x%x", *request_to_enroll); + return -1; + } + return 0; +} + + +static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num, + int mandatory) +{ + if (num == 0) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device " + "Type attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_wps_state(const u8 *wps_state, int mandatory) +{ + if (wps_state == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected " + "Setup State attribute missing"); + return -1; + } + return 0; + } + if (*wps_state != WPS_STATE_NOT_CONFIGURED && + *wps_state != WPS_STATE_CONFIGURED) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected " + "Setup State attribute value 0x%x", *wps_state); + return -1; + } + return 0; +} + + +static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked, + int mandatory) +{ + if (ap_setup_locked == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked " + "attribute missing"); + return -1; + } + return 0; + } + if (*ap_setup_locked > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked " + "attribute value 0x%x", *ap_setup_locked); + return -1; + } + return 0; +} + + +static int wps_validate_selected_registrar(const u8 *selected_registrar, + int mandatory) +{ + if (selected_registrar == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar " + "attribute missing"); + return -1; + } + return 0; + } + if (*selected_registrar > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar " + "attribute value 0x%x", *selected_registrar); + return -1; + } + return 0; +} + + +static int wps_validate_sel_reg_config_methods(const u8 *config_methods, + int wps2, int mandatory) +{ + u16 val; + + if (config_methods == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar " + "Configuration Methods attribute missing"); + return -1; + } + return 0; + } + + val = WPA_GET_BE16(config_methods); + if (!valid_config_methods(val, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar " + "Configuration Methods attribute value 0x%04x", + val); + return -1; + } + return 0; +} + + +static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len, + int mandatory) +{ + if (authorized_macs == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs " + "attribute missing"); + return -1; + } + return 0; + } + if (len > 30 && (len % ETH_ALEN) != 0) { + wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized " + "MACs attribute value", authorized_macs, len); + return -1; + } + return 0; +} + + +static int wps_validate_msg_type(const u8 *msg_type, int mandatory) +{ + if (msg_type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Message Type " + "attribute missing"); + return -1; + } + return 0; + } + if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type " + "attribute value 0x%x", *msg_type); + return -1; + } + return 0; +} + + +static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory) +{ + if (mac_addr == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address " + "attribute missing"); + return -1; + } + return 0; + } + if (mac_addr[0] & 0x01) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address " + "attribute value " MACSTR, MAC2STR(mac_addr)); + return -1; + } + return 0; +} + + +static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory) +{ + if (enrollee_nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_registrar_nonce(const u8 *registrar_nonce, + int mandatory) +{ + if (registrar_nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_public_key(const u8 *public_key, size_t len, + int mandatory) +{ + if (public_key == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Public Key " + "attribute missing"); + return -1; + } + return 0; + } + if (len != 192) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key " + "attribute length %d", (int) len); + return -1; + } + return 0; +} + + +static int num_bits_set(u16 val) +{ + int c; + for (c = 0; val; c++) + val &= val - 1; + return c; +} + + +static int wps_validate_auth_type_flags(const u8 *flags, int mandatory) +{ + u16 val; + + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(flags); + if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type " + "Flags attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_auth_type(const u8 *type, int mandatory) +{ + u16 val; + + if (type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(type); + if ((val & ~WPS_AUTH_TYPES) || val == 0 || + (num_bits_set(val) > 1 && + val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_encr_type_flags(const u8 *flags, int mandatory) +{ + u16 val; + + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(flags); + if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type " + "Flags attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_encr_type(const u8 *type, int mandatory) +{ + u16 val; + + if (type == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type " + "attribute missing"); + return -1; + } + return 0; + } + val = WPA_GET_BE16(type); + if ((val & ~WPS_ENCR_TYPES) || val == 0 || + (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type " + "attribute value 0x%04x", val); + return -1; + } + return 0; +} + + +static int wps_validate_conn_type_flags(const u8 *flags, int mandatory) +{ + if (flags == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type " + "Flags attribute missing"); + return -1; + } + return 0; + } + if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) || + !(*flags & WPS_CONN_ESS)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type " + "Flags attribute value 0x%02x", *flags); + return -1; + } + return 0; +} + + +static int wps_validate_os_version(const u8 *os_version, int mandatory) +{ + if (os_version == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: OS Version " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_authenticator(const u8 *authenticator, int mandatory) +{ + if (authenticator == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_hash1(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_hash2(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_hash1(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_hash2(const u8 *hash, int mandatory) +{ + if (hash == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_encr_settings(const u8 *encr_settings, size_t len, + int mandatory) +{ + if (encr_settings == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings " + "attribute missing"); + return -1; + } + return 0; + } + if (len < 16) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings " + "attribute length %d", (int) len); + return -1; + } + return 0; +} + + +static int wps_validate_settings_delay_time(const u8 *delay, int mandatory) +{ + if (delay == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_snonce1(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_r_snonce2(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_snonce1(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_e_snonce2(const u8 *nonce, int mandatory) +{ + if (nonce == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory) +{ + if (auth == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap " + "Authenticator attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory) +{ + if (ssid == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: SSID " + "attribute missing"); + return -1; + } + return 0; + } + if (ssid_len == 0 || ssid[ssid_len - 1] == 0) { + wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID " + "attribute value", ssid, ssid_len); + return -1; + } + return 0; +} + + +static int wps_validate_network_key_index(const u8 *idx, int mandatory) +{ + if (idx == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_network_idx(const u8 *idx, int mandatory) +{ + if (idx == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Index " + "attribute missing"); + return -1; + } + return 0; + } + return 0; +} + + +static int wps_validate_network_key(const u8 *key, size_t key_len, + const u8 *encr_type, int mandatory) +{ + if (key == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key " + "attribute missing"); + return -1; + } + return 0; + } + if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) && + key_len > 8 && key_len < 64 && key[key_len - 1] == 0) || + key_len > 64) { + wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network " + "Key attribute value", key, key_len); + return -1; + } + return 0; +} + + +static int wps_validate_network_key_shareable(const u8 *val, int mandatory) +{ + if (val == NULL) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Network Key " + "Shareable attribute missing"); + return -1; + } + return 0; + } + if (*val > 1) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key " + "Shareable attribute value 0x%x", *val); + return -1; + } + return 0; +} + + +static int wps_validate_cred(const u8 *cred, size_t len) +{ + struct wps_parse_attr attr; + struct wpabuf buf; + + if (cred == NULL) + return -1; + wpabuf_set(&buf, cred, len); + if (wps_parse_msg(&buf, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential"); + return -1; + } + + if (wps_validate_network_idx(attr.network_idx, 1) || + wps_validate_ssid(attr.ssid, attr.ssid_len, 1) || + wps_validate_auth_type(attr.auth_type, 1) || + wps_validate_encr_type(attr.encr_type, 1) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_network_key(attr.network_key, attr.network_key_len, + attr.encr_type, 1) || + wps_validate_mac_addr(attr.mac_addr, 1) || + wps_validate_network_key_shareable(attr.network_key_shareable, 0)) + { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential"); + return -1; + } + + + return 0; +} + + +static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num, + int mandatory) +{ + size_t i; + + if (num == 0) { + if (mandatory) { + wpa_printf(MSG_INFO, "WPS-STRICT: Credential " + "attribute missing"); + return -1; + } + return 0; + } + + for (i = 0; i < num; i++) { + if (wps_validate_cred(cred[i], len[i]) < 0) + return -1; + } + + return 0; +} + + +int wps_validate_beacon(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2, sel_reg; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "Beacon frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) || + wps_validate_selected_registrar(attr.selected_registrar, 0) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_uuid_e(attr.uuid_e, 0) || + wps_validate_rf_bands(attr.rf_bands, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame"); + return -1; + } + + return 0; +} + + +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe, + const u8 *addr) +{ + struct wps_parse_attr attr; + int wps2, sel_reg; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "%sProbe Response frame", probe ? "" : "Beacon/"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "%sProbe Response frame", probe ? "" : "Beacon/"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) || + wps_validate_selected_registrar(attr.selected_registrar, 0) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_response_type(attr.response_type, probe) || + wps_validate_uuid_e(attr.uuid_e, probe) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + probe) || + wps_validate_model_name(attr.model_name, attr.model_name_len, + probe) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + probe) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, probe) || + wps_validate_primary_dev_type(attr.primary_dev_type, probe) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) || + wps_validate_ap_config_methods(attr.config_methods, wps2, probe) || + wps_validate_rf_bands(attr.rf_bands, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response " + "frame from " MACSTR, probe ? "" : "Beacon/", + MAC2STR(addr)); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "Probe Request frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "Probe Request frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_request_type(attr.request_type, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) || + wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + wps2) || + wps_validate_model_name(attr.model_name, attr.model_name_len, + wps2) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + wps2) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) || + wps_validate_request_to_enroll(attr.request_to_enroll, 0) || + wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type, + 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request " + "frame from " MACSTR, MAC2STR(addr)); + return -1; + } + + return 0; +} + + +int wps_validate_assoc_req(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "(Re)Association Request frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "(Re)Association Request frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_request_type(attr.request_type, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association " + "Request frame"); + return -1; + } + + return 0; +} + + +int wps_validate_assoc_resp(const struct wpabuf *wps_ie) +{ + struct wps_parse_attr attr; + int wps2; + + if (wps_ie == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in " + "(Re)Association Response frame"); + return -1; + } + if (wps_parse_msg(wps_ie, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in " + "(Re)Association Response frame"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_response_type(attr.response_type, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association " + "Response frame"); + return -1; + } + + return 0; +} + + +int wps_validate_m1(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M1"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_uuid_e(attr.uuid_e, 1) || + wps_validate_mac_addr(attr.mac_addr, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_public_key(attr.public_key, attr.public_key_len, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_wps_state(attr.wps_state, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_request_to_enroll(attr.request_to_enroll, 0)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m2(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M2"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_uuid_r(attr.uuid_r, 1) || + wps_validate_public_key(attr.public_key, attr.public_key_len, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_dev_password_id(attr.dev_password_id, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m2d(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M2D"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_uuid_r(attr.uuid_r, 1) || + wps_validate_auth_type_flags(attr.auth_type_flags, 1) || + wps_validate_encr_type_flags(attr.encr_type_flags, 1) || + wps_validate_conn_type_flags(attr.conn_type_flags, 1) || + wps_validate_config_methods(attr.config_methods, wps2, 1) || + wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len, + 1) || + wps_validate_model_name(attr.model_name, attr.model_name_len, 1) || + wps_validate_model_number(attr.model_number, attr.model_number_len, + 1) || + wps_validate_serial_number(attr.serial_number, + attr.serial_number_len, 1) || + wps_validate_primary_dev_type(attr.primary_dev_type, 1) || + wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) || + wps_validate_rf_bands(attr.rf_bands, 1) || + wps_validate_assoc_state(attr.assoc_state, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_os_version(attr.os_version, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m3(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M3"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_e_hash1(attr.e_hash1, 1) || + wps_validate_e_hash2(attr.e_hash2, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m4(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M4"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_r_hash1(attr.r_hash1, 1) || + wps_validate_r_hash2(attr.r_hash2, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M4 encrypted settings"); + return -1; + } + + if (wps_validate_r_snonce1(attr.r_snonce1, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m5(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M5"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M5 encrypted settings"); + return -1; + } + + if (wps_validate_e_snonce1(attr.e_snonce1, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m6(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M6"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M6 encrypted settings"); + return -1; + } + + if (wps_validate_r_snonce2(attr.r_snonce2, 1) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m7(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M7"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_settings_delay_time(attr.settings_delay_time, 0) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M7 encrypted settings"); + return -1; + } + + if (wps_validate_e_snonce2(attr.e_snonce2, 1) || + wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) || + wps_validate_mac_addr(attr.mac_addr, !ap) || + wps_validate_auth_type(attr.auth_type, !ap) || + wps_validate_encr_type(attr.encr_type, !ap) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_network_key(attr.network_key, attr.network_key_len, + attr.encr_type, !ap) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m8(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M8"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_encr_settings(attr.encr_settings, + attr.encr_settings_len, 1) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authenticator(attr.authenticator, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2) +{ + struct wps_parse_attr attr; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted " + "settings"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in M8 encrypted settings"); + return -1; + } + + if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) || + wps_validate_auth_type(attr.auth_type, ap) || + wps_validate_encr_type(attr.encr_type, ap) || + wps_validate_network_key_index(attr.network_key_idx, 0) || + wps_validate_mac_addr(attr.mac_addr, ap) || + wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred, + !ap) || + wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted " + "settings"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_ack(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_ACK"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_nack(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_NACK"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_config_error(attr.config_error, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_wsc_done(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in WSC_Done"); + return -1; + } + + wps2 = attr.version2 != NULL; + if (wps_validate_version(attr.version, 1) || + wps_validate_msg_type(attr.msg_type, 1) || + wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) || + wps_validate_registrar_nonce(attr.registrar_nonce, 1) || + wps_validate_version2(attr.version2, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} + + +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs) +{ + struct wps_parse_attr attr; + int wps2; + int sel_reg; + + if (tlvs == NULL) { + wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in " + "SetSelectedRegistrar"); + return -1; + } + if (wps_parse_msg(tlvs, &attr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes " + "in SetSelectedRegistrar"); + return -1; + } + + wps2 = attr.version2 != NULL; + sel_reg = attr.selected_registrar != NULL && + *attr.selected_registrar != 0; + if (wps_validate_version(attr.version, 1) || + wps_validate_dev_password_id(attr.dev_password_id, sel_reg) || + wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods, + wps2, sel_reg) || + wps_validate_version2(attr.version2, wps2) || + wps_validate_authorized_macs(attr.authorized_macs, + attr.authorized_macs_len, wps2) || + wps_validate_uuid_r(attr.uuid_r, wps2)) { + wpa_printf(MSG_INFO, "WPS-STRICT: Invalid " + "SetSelectedRegistrar"); +#ifdef WPS_STRICT_WPS2 + if (wps2) + return -1; +#else /* WPS_STRICT_WPS2 */ + return -1; +#endif /* WPS_STRICT_WPS2 */ + } + + return 0; +} |