From: arron.wang Date: Thu, 7 Jun 2012 23:52:41 +0000 (+0800) Subject: Merge with wpa_supplicant 1.0 stable release X-Git-Tag: 20120702.1058~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=80daf8e2bef20606685c9ca4cc7b3ea19f68ab4a;p=profile%2Fivi%2Fwpa_supplicant.git Merge with wpa_supplicant 1.0 stable release --- diff --git a/README b/README index 9c6be85..abdc18c 100644 --- a/README +++ b/README @@ -1,19 +1,75 @@ -wpa_supplicant and hostapd v0.6.x ---------------------------------- +wpa_supplicant and hostapd +-------------------------- -Copyright (c) 2002-2007, Jouni Malinen and contributors +Copyright (c) 2002-2012, Jouni Malinen and contributors All Rights Reserved. -These program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. +These programs are dual-licensed under both the GPL version 2 and BSD +license (the one with advertisement clause removed). Either license +may be used at your option. + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. This package may include either wpa_supplicant, hostapd, or both. See README file respective subdirectories (wpa_supplicant/README or hostapd/README) for more details. -Source code files have been moved around in v0.6.x releases and -compared to earlier releases, the programs are now build by first -going to a subdirectory (wpa_supplicant or hostapd) and creating -build configuration (.config) and running 'make' there (for -Linux/BSD/cygwin builds). +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +GPL v2: + +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. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed, used, and modified +under the terms of BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packaging/wpa_supplicant.changes b/packaging/wpa_supplicant.changes index 010796e..443ac6d 100644 --- a/packaging/wpa_supplicant.changes +++ b/packaging/wpa_supplicant.changes @@ -1,3 +1,6 @@ +* Fri Jun 8 2012 Arron - 1.0 +- Merge with wpa_supplicant 1.0 stable release + * Wed May 30 2012 Junfeng Dong - Clean up spec file for packaging. diff --git a/packaging/wpa_supplicant.spec b/packaging/wpa_supplicant.spec index 43b4db9..697d225 100644 --- a/packaging/wpa_supplicant.spec +++ b/packaging/wpa_supplicant.spec @@ -1,6 +1,6 @@ Name: wpa_supplicant Summary: WPA/WPA2/IEEE 802.1X Supplicant -Version: 0.7.3 +Version: 1.0 Release: 1 Group: System/Base License: BSD diff --git a/src/Makefile b/src/Makefile index f47da7b..d73a175 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps +SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps all: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 7939c68..ad20fea 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -23,6 +23,7 @@ #include "ieee802_1x.h" #include "ap_config.h" #include "sta_info.h" +#include "ap_drv_ops.h" #include "accounting.h" @@ -186,7 +187,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, struct sta_info *sta, struct hostap_sta_driver_data *data) { - if (hapd->drv.read_sta_data(hapd, data, sta->addr)) + if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; if (sta->last_rx_bytes > data->rx_bytes) @@ -235,6 +236,7 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; + struct os_time t; int interval; if (sta->acct_session_started) @@ -246,10 +248,11 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) "starting accounting session %08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); - time(&sta->acct_session_start); + os_get_time(&t); + sta->acct_session_start = t.sec; sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; - hapd->drv.sta_clear_stats(hapd, sta->addr); + hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) return; @@ -262,8 +265,9 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) hapd, sta); msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); - if (msg) - radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); + if (msg && + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) + radius_msg_free(msg); sta->acct_session_started = 1; } @@ -275,6 +279,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; + struct os_time now; u32 gigawords; if (!hapd->conf->radius->acct_server) @@ -288,8 +293,9 @@ static void accounting_sta_report(struct hostapd_data *hapd, return; } + os_get_time(&now); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, - time(NULL) - sta->acct_session_start)) { + now.sec - sta->acct_session_start)) { printf("Could not add Acct-Session-Time\n"); goto fail; } @@ -344,7 +350,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - time(NULL))) { + now.sec)) { printf("Could not add Event-Timestamp\n"); goto fail; } @@ -359,9 +365,10 @@ static void accounting_sta_report(struct hostapd_data *hapd, goto fail; } - radius_client_send(hapd->radius, msg, - stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, - sta->addr); + if (radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr) < 0) + goto fail; return; fail: @@ -464,7 +471,8 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) return; } - radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); + if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) + radius_msg_free(msg); } @@ -475,9 +483,12 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) */ int accounting_init(struct hostapd_data *hapd) { + struct os_time now; + /* Acct-Session-Id should be unique over reboots. If reliable clock is * not available, this could be replaced with reboot counter, etc. */ - hapd->acct_session_id_hi = time(NULL); + os_get_time(&now); + hapd->acct_session_id_hi = now.sec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 5996993..cfb6b2d 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -74,6 +74,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->max_listen_interval = 65535; + bss->pwd_group = 19; /* ECC: GF(p=256) */ + #ifdef CONFIG_IEEE80211W bss->assoc_sa_query_max_timeout = 1000; bss->assoc_sa_query_retry_timeout = 201; @@ -84,14 +86,22 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->pac_key_lifetime = 7 * 24 * 60 * 60; bss->pac_key_refresh_time = 1 * 24 * 60 * 60; #endif /* EAP_SERVER_FAST */ + + /* Set to -1 as defaults depends on HT in setup */ + bss->wmm_enabled = -1; + +#ifdef CONFIG_IEEE80211R + bss->ft_over_ds = 1; +#endif /* CONFIG_IEEE80211R */ } struct hostapd_config * hostapd_config_defaults(void) { +#define ecw2cw(ecw) ((1 << (ecw)) - 1) + struct hostapd_config *conf; struct hostapd_bss_config *bss; - int i; const int aCWmin = 4, aCWmax = 10; const struct hostapd_wmm_ac_params ac_bk = { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ @@ -101,6 +111,17 @@ struct hostapd_config * hostapd_config_defaults(void) { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; + const struct hostapd_tx_queue_params txq_bk = + { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; + const struct hostapd_tx_queue_params txq_be = + { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0}; + const struct hostapd_tx_queue_params txq_vi = + { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30}; + const struct hostapd_tx_queue_params txq_vo = + { 1, (ecw2cw(aCWmin) + 1) / 4 - 1, + (ecw2cw(aCWmin) + 1) / 2 - 1, 15}; + +#undef ecw2cw conf = os_zalloc(sizeof(*conf)); bss = os_zalloc(sizeof(*bss)); @@ -129,14 +150,16 @@ struct hostapd_config * hostapd_config_defaults(void) conf->fragm_threshold = -1; /* user driver default: 2346 */ conf->send_probe_response = 1; - for (i = 0; i < NUM_TX_QUEUES; i++) - conf->tx_queue[i].aifs = -1; /* use hw default */ - conf->wmm_ac_params[0] = ac_be; conf->wmm_ac_params[1] = ac_bk; conf->wmm_ac_params[2] = ac_vi; conf->wmm_ac_params[3] = ac_vo; + conf->tx_queue[0] = txq_vo; + conf->tx_queue[1] = txq_vi; + conf->tx_queue[2] = txq_be; + conf->tx_queue[3] = txq_bk; + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; return conf; @@ -403,6 +426,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) ssid->dyn_vlan_keys = NULL; } + os_free(conf->time_zone); + #ifdef CONFIG_IEEE80211R { struct ft_remote_r0kh *r0kh, *r0kh_prev; @@ -433,7 +458,6 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_name); os_free(conf->model_number); os_free(conf->serial_number); - os_free(conf->device_type); os_free(conf->config_methods); os_free(conf->ap_pin); os_free(conf->extra_cred); @@ -445,6 +469,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_url); os_free(conf->upc); #endif /* CONFIG_WPS */ + + os_free(conf->roaming_consortium); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index f509b5b..fe20fc2 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -18,6 +18,7 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "wps/wps.h" #define MAX_STA_COUNT 2007 #define MAX_VLAN_ID 4094 @@ -123,14 +124,13 @@ struct hostapd_eap_user { }; -#define NUM_TX_QUEUES 8 +#define NUM_TX_QUEUES 4 struct hostapd_tx_queue_params { int aifs; int cwmin; int cwmax; int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ - int configured; }; struct hostapd_wmm_ac_params { @@ -142,12 +142,20 @@ struct hostapd_wmm_ac_params { }; +#define MAX_ROAMING_CONSORTIUM_LEN 15 + +struct hostapd_roaming_consortium { + u8 len; + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; + char wds_bridge[IFNAMSIZ + 1]; enum hostapd_logger_level logger_syslog_level, logger_stdout_level; @@ -198,6 +206,7 @@ struct hostapd_bss_config { struct mac_acl_entry *deny_mac; int num_deny_mac; int wds_sta; + int isolate; int auth_algs; /* bitfield of allowed IEEE 802.11 authentication * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ @@ -231,6 +240,7 @@ struct hostapd_bss_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #endif /* CONFIG_IEEE80211R */ char *ctrl_interface; /* directory for UNIX domain sockets */ @@ -254,6 +264,8 @@ struct hostapd_bss_config { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + int fragment_size; + u16 pwd_group; char *radius_server_clients; int radius_server_auth_port; @@ -283,6 +295,7 @@ struct hostapd_bss_config { */ u16 max_listen_interval; + int disable_pmksa_caching; int okc; /* Opportunistic Key Caching */ int wps_state; @@ -295,7 +308,7 @@ struct hostapd_bss_config { char *model_name; char *model_number; char *serial_number; - char *device_type; + u8 device_type[WPS_DEV_TYPE_LEN]; char *config_methods; u8 os_version[4]; char *ap_pin; @@ -311,7 +324,43 @@ struct hostapd_bss_config { char *model_description; char *model_url; char *upc; + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; #endif /* CONFIG_WPS */ + int pbc_in_m1; + +#define P2P_ENABLED BIT(0) +#define P2P_GROUP_OWNER BIT(1) +#define P2P_GROUP_FORMATION BIT(2) +#define P2P_MANAGE BIT(3) +#define P2P_ALLOW_CROSS_CONNECTION BIT(4) + int p2p; + + int disassoc_low_ack; + +#define TDLS_PROHIBIT BIT(0) +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) + int tdls; + int disable_11n; + + /* IEEE 802.11v */ + int time_advertisement; + char *time_zone; + + /* IEEE 802.11u - Interworking */ + int interworking; + int access_network_type; + int internet; + int asra; + int esr; + int uesa; + int venue_info_set; + u8 venue_group; + u8 venue_type; + u8 hessid[ETH_ALEN]; + + /* IEEE 802.11u - Roaming Consortium list */ + unsigned int roaming_consortium_count; + struct hostapd_roaming_consortium *roaming_consortium; }; @@ -332,12 +381,6 @@ struct hostapd_config { LONG_PREAMBLE = 0, SHORT_PREAMBLE = 1 } preamble; - enum { - CTS_PROTECTION_AUTOMATIC = 0, - CTS_PROTECTION_FORCE_ENABLED = 1, - CTS_PROTECTION_FORCE_DISABLED = 2, - CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3, - } cts_protection_type; int *supported_rates; int *basic_rates; @@ -371,6 +414,7 @@ struct hostapd_config { u16 ht_capab; int ieee80211n; int secondary_channel; + int require_ht; }; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index f264a3e..01c28b9 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -17,14 +17,16 @@ #include "utils/common.h" #include "drivers/driver.h" #include "common/ieee802_11_defs.h" +#include "wps/wps.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" +#include "p2p_hostapd.h" #include "ap_drv_ops.h" -static int hostapd_sta_flags_to_drv(int flags) +u32 hostapd_sta_flags_to_drv(u32 flags) { int res = 0; if (flags & WLAN_STA_AUTHORIZED) @@ -39,45 +41,166 @@ static int hostapd_sta_flags_to_drv(int flags) } -static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon_ret, + struct wpabuf **proberesp_ret, + struct wpabuf **assocresp_ret) { - struct wpabuf *beacon, *proberesp; - int ret; + struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL; + u8 buf[200], *pos; - if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) - return 0; + *beacon_ret = *proberesp_ret = *assocresp_ret = NULL; - beacon = hapd->wps_beacon_ie; - proberesp = hapd->wps_probe_resp_ie; + pos = buf; + pos = hostapd_eid_time_adv(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + } + pos = hostapd_eid_time_zone(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } - ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp); + pos = buf; + pos = hostapd_eid_ext_capab(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&assocresp, pos - buf) != 0) + goto fail; + wpabuf_put_data(assocresp, buf, pos - buf); + } + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } - return ret; + if (hapd->wps_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->wps_beacon_ie); + } + + if (hapd->wps_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->wps_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); + } + +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); + } + + if (hapd->p2p_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&beacon, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(beacon, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(beacon, p - start); + } + + if (wpabuf_resize(&proberesp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(proberesp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(proberesp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WPS2 + if (hapd->conf->wps_state) { + struct wpabuf *a = wps_build_assoc_resp_ie(); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&assocresp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(assocresp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(assocresp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + + *beacon_ret = beacon; + *proberesp_ret = proberesp; + *assocresp_ret = assocresp; + + return 0; + +fail: + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); + return -1; } -static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, - size_t len) +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp) { - if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) - return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len); + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); } -static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, - const u8 *data, size_t data_len, int encrypt) +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) { - if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + struct wpabuf *beacon, *proberesp, *assocresp; + int ret; + + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) return 0; - return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, - data_len, encrypt, - hapd->own_addr); + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + return -1; + + ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp, + assocresp); + + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + + return ret; } -static int hostapd_set_authorized(struct hostapd_data *hapd, - struct sta_info *sta, int authorized) +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) { if (authorized) { return hostapd_sta_set_flags(hapd, sta->addr, @@ -92,39 +215,7 @@ static int hostapd_set_authorized(struct hostapd_data *hapd, } -static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd, - enum wpa_alg alg, const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - if (hapd->driver == NULL || hapd->driver->set_key == NULL) - return 0; - return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, - key_idx, set_tx, seq, seq_len, key, - key_len); -} - - -static int hostapd_read_sta_data(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, - const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) - return -1; - return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); -} - - -static int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) - return 0; - return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); -} - - -static int hostapd_set_sta_flags(struct hostapd_data *hapd, - struct sta_info *sta) +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta) { int set_flags, total_flags, flags_and, flags_or; total_flags = hostapd_sta_flags_to_drv(sta->flags); @@ -140,8 +231,8 @@ static int hostapd_set_sta_flags(struct hostapd_data *hapd, } -static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, - const char *ifname, int enabled) +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled) { struct wpa_bss_params params; os_memset(¶ms, 0, sizeof(params)); @@ -154,166 +245,80 @@ static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, params.wpa_pairwise = hapd->conf->wpa_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; +#ifdef CONFIG_IEEE80211W + params.ieee80211w = hapd->conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ } return hostapd_set_ieee8021x(hapd, ¶ms); } -static int hostapd_set_radius_acl_auth(struct hostapd_data *hapd, - const u8 *mac, int accepted, - u32 session_timeout) -{ - if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) - return 0; - return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, - session_timeout); -} - - -static int hostapd_set_radius_acl_expire(struct hostapd_data *hapd, - const u8 *mac) -{ - if (hapd->driver == NULL || - hapd->driver->set_radius_acl_expire == NULL) - return 0; - return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); -} - - -static int hostapd_set_bss_params(struct hostapd_data *hapd, - int use_protection) -{ - int ret = 0; - int preamble; -#ifdef CONFIG_IEEE80211N - u8 buf[60], *ht_capab, *ht_oper, *pos; - - pos = buf; - ht_capab = pos; - pos = hostapd_eid_ht_capabilities(hapd, pos); - ht_oper = pos; - pos = hostapd_eid_ht_operation(hapd, pos); - if (pos > ht_oper && ht_oper > ht_capab && - hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1], - ht_oper + 2, ht_oper[1])) { - wpa_printf(MSG_ERROR, "Could not set HT capabilities " - "for kernel driver"); - ret = -1; - } - -#endif /* CONFIG_IEEE80211N */ - - if (hostapd_set_cts_protect(hapd, use_protection)) { - wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " - "driver"); - ret = -1; - } - - if (hapd->iface->current_mode && - hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && - hostapd_set_short_slot_time(hapd, - hapd->iface->num_sta_no_short_slot_time - > 0 ? 0 : 1)) { - wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " - "in kernel driver"); - ret = -1; - } - - if (hapd->iface->num_sta_no_short_preamble == 0 && - hapd->iconf->preamble == SHORT_PREAMBLE) - preamble = SHORT_PREAMBLE; - else - preamble = LONG_PREAMBLE; - if (hostapd_set_preamble(hapd, preamble)) { - wpa_printf(MSG_ERROR, "Could not set preamble for kernel " - "driver"); - ret = -1; - } - - return ret; -} - - -static int hostapd_set_beacon(struct hostapd_data *hapd, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int) -{ - if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) - return 0; - return hapd->driver->set_beacon(hapd->drv_priv, - head, head_len, tail, tail_len, - dtim_period, beacon_int); -} - - -static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) { char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; - return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL, - force_ifname, if_addr); + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, + NULL, NULL, force_ifname, if_addr, NULL); } -static int hostapd_vlan_if_remove(struct hostapd_data *hapd, - const char *ifname) + +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) { return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); } -static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, - int aid, int val) +int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, + int val) { + const char *bridge = NULL; + if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) return 0; - return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val); + if (hapd->conf->wds_bridge[0]) + bridge = hapd->conf->wds_bridge; + else if (hapd->conf->bridge[0]) + bridge = hapd->conf->bridge; + return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, + bridge); } -static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int vlan_id) +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg) { - if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) return 0; - return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, - vlan_id); + return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); } -static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len) { - if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; - return hapd->driver->get_inact_sec(hapd->drv_priv, addr); + return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, + seq, status, ie, len); } -static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, - int reason) +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len) { - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL) return 0; - return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, - reason); + return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, + reassoc, status, ie, len); } -static int hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, - int reason) -{ - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) - return 0; - return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, - reason); -} - - -static int hostapd_sta_add(struct hostapd_data *hapd, - const u8 *addr, u16 aid, u16 capability, - const u8 *supp_rates, size_t supp_rates_len, - u16 listen_interval, - const struct ieee80211_ht_capabilities *ht_capab) +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + u32 flags) { struct hostapd_sta_add_params params; @@ -330,52 +335,18 @@ static int hostapd_sta_add(struct hostapd_data *hapd, params.supp_rates_len = supp_rates_len; params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; + params.flags = hostapd_sta_flags_to_drv(flags); return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } -static int hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) - return 0; - return hapd->driver->sta_remove(hapd->drv_priv, addr); -} - - -static int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen) { - if (hapd->driver == NULL || - hapd->driver->hapd_set_countermeasures == NULL) + if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) return 0; - return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); -} - - -void hostapd_set_driver_ops(struct hostapd_driver_ops *ops) -{ - ops->set_ap_wps_ie = hostapd_set_ap_wps_ie; - ops->send_mgmt_frame = hostapd_send_mgmt_frame; - ops->send_eapol = hostapd_send_eapol; - ops->set_authorized = hostapd_set_authorized; - ops->set_key = hostapd_set_key; - ops->read_sta_data = hostapd_read_sta_data; - ops->sta_clear_stats = hostapd_sta_clear_stats; - ops->set_sta_flags = hostapd_set_sta_flags; - ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x; - ops->set_radius_acl_auth = hostapd_set_radius_acl_auth; - ops->set_radius_acl_expire = hostapd_set_radius_acl_expire; - ops->set_bss_params = hostapd_set_bss_params; - ops->set_beacon = hostapd_set_beacon; - ops->vlan_if_add = hostapd_vlan_if_add; - ops->vlan_if_remove = hostapd_vlan_if_remove; - ops->set_wds_sta = hostapd_set_wds_sta; - ops->set_sta_vlan = hostapd_set_sta_vlan; - ops->get_inact_sec = hostapd_get_inact_sec; - ops->sta_deauth = hostapd_sta_deauth; - ops->sta_disassoc = hostapd_sta_disassoc; - ops->sta_add = hostapd_sta_add; - ops->sta_remove = hostapd_sta_remove; - ops->set_countermeasures = hostapd_set_countermeasures; + return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, + tspec_ielen); } @@ -414,12 +385,14 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr) + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge) { if (hapd->driver == NULL || hapd->driver->if_add == NULL) return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, - bss_ctx, drv_priv, force_ifname, if_addr); + bss_ctx, drv_priv, force_ifname, if_addr, + bridge); } @@ -521,30 +494,6 @@ int hostapd_set_country(struct hostapd_data *hapd, const char *country) } -int hostapd_set_cts_protect(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) - return 0; - return hapd->driver->set_cts_protect(hapd->drv_priv, value); -} - - -int hostapd_set_preamble(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) - return 0; - return hapd->driver->set_preamble(hapd->drv_priv, value); -} - - -int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) - return 0; - return hapd->driver->set_short_slot_time(hapd->drv_priv, value); -} - - int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time) { @@ -555,15 +504,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, } -int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, - const u8 *mask) -{ - if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) - return 1; - return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); -} - - struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags) @@ -584,19 +524,6 @@ int hostapd_driver_commit(struct hostapd_data *hapd) } -int hostapd_set_ht_params(struct hostapd_data *hapd, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len) -{ - if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || - ht_capab == NULL || ht_oper == NULL) - return 0; - return hapd->driver->set_ht_params(hapd->drv_priv, - ht_capab, ht_capab_len, - ht_oper, ht_oper_len); -} - - int hostapd_drv_none(struct hostapd_data *hapd) { return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; @@ -619,3 +546,56 @@ struct wpa_scan_results * hostapd_driver_get_scan_results( return hapd->driver->get_scan_results2(hapd->drv_priv); return NULL; } + + +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + if (hapd->driver && hapd->driver->set_noa) + return hapd->driver->set_noa(hapd->drv_priv, count, start, + duration); + return -1; +} + + +int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (hapd->driver == NULL || hapd->driver->set_key == NULL) + return 0; + return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, key, + key_len); +} + + +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len); +} + + +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, + reason); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 9b75d09..66e2cb8 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -18,8 +18,32 @@ enum wpa_driver_if_type; struct wpa_bss_params; struct wpa_driver_scan_params; +struct ieee80211_ht_capabilities; -void hostapd_set_driver_ops(struct hostapd_driver_ops *ops); +u32 hostapd_sta_flags_to_drv(u32 flags); +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon, + struct wpabuf **proberesp, + struct wpabuf **assocresp); +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp); +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta); +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled); +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); +int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, + int val); +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + u32 flags); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -27,7 +51,8 @@ int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len); int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr); + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge); int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname); int hostapd_set_ieee8021x(struct hostapd_data *hapd, @@ -44,24 +69,149 @@ int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, int *basic_rates, int mode); int hostapd_set_country(struct hostapd_data *hapd, const char *country); -int hostapd_set_cts_protect(struct hostapd_data *hapd, int value); -int hostapd_set_preamble(struct hostapd_data *hapd, int value); -int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value); int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); -int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, - const u8 *mask); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags); int hostapd_driver_commit(struct hostapd_data *hapd); -int hostapd_set_ht_params(struct hostapd_data *hapd, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params); struct wpa_scan_results * hostapd_driver_get_scan_results( struct hostapd_data *hapd); +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +int hostapd_drv_set_key(const char *ifname, + struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len); +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg); +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen); + + +#include "drivers/driver.h" + +static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, + int enabled) +{ + if (hapd->driver == NULL || + hapd->driver->hapd_set_countermeasures == NULL) + return 0; + return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); +} + +static inline int hostapd_drv_set_sta_vlan(const char *ifname, + struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, + vlan_id); +} + +static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd, + const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + u32 flags) +{ + if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + return 0; + return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, + data_len, encrypt, + hapd->own_addr, flags); +} + +static inline int hostapd_drv_read_sta_data( + struct hostapd_data *hapd, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + +static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ap == NULL) + return 0; + return hapd->driver->set_ap(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + +static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd, + const u8 *mac) +{ + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + +static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, + int auth_algs) +{ + if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) + return 0; + return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); +} + +static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, + const u8 *own_addr, const u8 *addr, + int qos) +{ + if (hapd->driver == NULL || hapd->driver->poll_client == NULL) + return; + hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); +} #endif /* AP_DRV_OPS */ diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index 5297dbf..9b9fc9e 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -227,6 +227,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, struct hostapd_frame_info *fi) { struct ap_info *ap; + struct os_time now; int new_ap = 0; size_t len; int set_beacon = 0; @@ -292,7 +293,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface, ap->ht_support = 0; ap->num_beacons++; - time(&ap->last_beacon); + os_get_time(&now); + ap->last_beacon = now.sec; if (fi) { ap->ssi_signal = fi->ssi_signal; ap->datarate = fi->datarate; @@ -331,7 +333,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; - time_t now; + struct os_time now; struct ap_info *ap; int set_beacon = 0; @@ -340,12 +342,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) if (!iface->ap_list) return; - time(&now); + os_get_time(&now); while (iface->ap_list) { ap = iface->ap_list->prev; if (ap->last_beacon + iface->conf->ap_table_expiration_time >= - now) + now.sec) break; ap_free_ap(iface, ap); diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h index f49f58b..6df8981 100644 --- a/src/ap/ap_list.h +++ b/src/ap/ap_list.h @@ -45,7 +45,7 @@ struct ap_info { int ht_support; unsigned int num_beacons; /* number of beacon frames received */ - time_t last_beacon; + os_time_t last_beacon; int already_seen; /* whether API call AP-NEW has already fetched * information about this AP */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 0ab0668..7c87fde 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -119,6 +119,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.get_eap_user = hostapd_radius_get_eap_user; srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; + srv.pwd_group = conf->pwd_group; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 004cc8a..63b708a 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -22,15 +22,21 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "drivers/driver.h" +#include "wps/wps_defs.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "wpa_auth.h" #include "wmm.h" #include "ap_config.h" #include "sta_info.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" #include "beacon.h" +#ifdef NEED_AP_MLME + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -39,23 +45,11 @@ static u8 ieee802_11_erp_info(struct hostapd_data *hapd) hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) return 0; - switch (hapd->iconf->cts_protection_type) { - case CTS_PROTECTION_FORCE_ENABLED: - erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; - break; - case CTS_PROTECTION_FORCE_DISABLED: - erp = 0; - break; - case CTS_PROTECTION_AUTOMATIC: - if (hapd->iface->olbc) - erp |= ERP_INFO_USE_PROTECTION; - /* continue */ - case CTS_PROTECTION_AUTOMATIC_NO_OLBC: - if (hapd->iface->num_sta_non_erp > 0) { - erp |= ERP_INFO_NON_ERP_PRESENT | - ERP_INFO_USE_PROTECTION; - } - break; + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; } if (hapd->iface->num_sta_no_short_preamble > 0 || hapd->iconf->preamble == LONG_PREAMBLE) @@ -178,8 +172,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, } -static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, - struct sta_info *sta) +static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) { const u8 *ie; size_t ielen; @@ -207,11 +200,14 @@ void handle_probe_req(struct hostapd_data *hapd, size_t i; ie = mgmt->u.probe_req.variable; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - mgmt->sa, ie, ie_len) > 0) + mgmt->sa, mgmt->da, mgmt->bssid, + ie, ie_len) > 0) return; if (!hapd->iconf->send_probe_response) @@ -233,6 +229,33 @@ void handle_probe_req(struct hostapd_data *hapd, return; } +#ifdef CONFIG_P2P + if (hapd->p2p && elems.wps_ie) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); + if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Requested Device " + "Type"); + wpabuf_free(wps); + return; + } + wpabuf_free(wps); + } + + if (hapd->p2p && elems.p2p) { + struct wpabuf *p2p; + p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); + if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Device ID"); + wpabuf_free(p2p); + return; + } + wpabuf_free(p2p); + } +#endif /* CONFIG_P2P */ + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " "broadcast SSID ignored", MAC2STR(mgmt->sa)); @@ -241,6 +264,16 @@ void handle_probe_req(struct hostapd_data *hapd, sta = ap_get_sta(hapd, mgmt->sa); +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) { + /* Process P2P Wildcard SSID like Wildcard SSID */ + elems.ssid_len = 0; + } +#endif /* CONFIG_P2P */ + if (elems.ssid_len == 0 || (elems.ssid_len == hapd->conf->ssid.ssid_len && os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == @@ -257,12 +290,43 @@ void handle_probe_req(struct hostapd_data *hapd, ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s'", - MAC2STR(mgmt->sa), ssid_txt); + " for foreign SSID '%s' (DA " MACSTR ")", + MAC2STR(mgmt->sa), ssid_txt, + MAC2STR(mgmt->da)); } return; } +#ifdef CONFIG_INTERWORKING + if (elems.interworking && elems.interworking_len >= 1) { + u8 ant = elems.interworking[0] & 0x0f; + if (ant != INTERWORKING_ANT_WILDCARD && + ant != hapd->conf->access_network_type) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching ANT %u ignored", + MAC2STR(mgmt->sa), ant); + return; + } + } + + if (elems.interworking && + (elems.interworking_len == 7 || elems.interworking_len == 9)) { + const u8 *hessid; + if (elems.interworking_len == 7) + hessid = elems.interworking + 1; + else + hessid = elems.interworking + 1 + 2; + if (!is_broadcast_ether_addr(hessid) && + os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching HESSID " MACSTR + " ignored", + MAC2STR(mgmt->sa), MAC2STR(hessid)); + return; + } + } +#endif /* CONFIG_INTERWORKING */ + /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ #define MAX_PROBERESP_LEN 768 @@ -271,6 +335,10 @@ void handle_probe_req(struct hostapd_data *hapd, if (hapd->wps_probe_resp_ie) buflen += wpabuf_len(hapd->wps_probe_resp_ie); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (hapd->p2p_probe_resp_ie) + buflen += wpabuf_len(hapd->p2p_probe_resp_ie); +#endif /* CONFIG_P2P */ resp = os_zalloc(buflen); if (resp == NULL) return; @@ -310,13 +378,22 @@ void handle_probe_req(struct hostapd_data *hapd, pos = hostapd_eid_ext_supp_rates(hapd, pos); /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); + pos = hostapd_eid_wpa(hapd, pos, epos - pos); #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ + pos = hostapd_eid_ext_capab(hapd, pos); + + pos = hostapd_eid_time_adv(hapd, pos); + pos = hostapd_eid_time_zone(hapd, pos); + + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -328,23 +405,48 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_WPS */ - if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0) +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && elems.p2p && + hapd->p2p_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), + wpabuf_len(hapd->p2p_probe_resp_ie)); + pos += wpabuf_len(hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + pos = hostapd_eid_p2p_manage(hapd, pos); +#endif /* CONFIG_P2P_MANAGER */ + + if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0) perror("handle_probe_req: send"); os_free(resp); - wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " + wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " "SSID", MAC2STR(mgmt->sa), elems.ssid_len == 0 ? "broadcast" : "our"); } +#endif /* NEED_AP_MLME */ + void ieee802_11_set_beacon(struct hostapd_data *hapd) { - struct ieee80211_mgmt *head; - u8 *pos, *tail, *tailpos; + struct ieee80211_mgmt *head = NULL; + u8 *tail = NULL; + size_t head_len = 0, tail_len = 0; + struct wpa_driver_ap_params params; + struct wpabuf *beacon, *proberesp, *assocresp; +#ifdef NEED_AP_MLME u16 capab_info; - size_t head_len, tail_len; + u8 *pos, *tailpos; +#endif /* NEED_AP_MLME */ + + hapd->beacon_set_done = 1; + +#ifdef NEED_AP_MLME #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -354,6 +456,10 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->conf->wps_state && hapd->wps_beacon_ie) tail_len += wpabuf_len(hapd->wps_beacon_ie); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) + tail_len += wpabuf_len(hapd->p2p_beacon_ie); +#endif /* CONFIG_P2P */ tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -412,13 +518,25 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) /* RSN, MDIE, WPA */ tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos, NULL); + tailpos); #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ + tailpos = hostapd_eid_ext_capab(hapd, tailpos); + + /* + * TODO: Time Advertisement element should only be included in some + * DTIM Beacon frames. + */ + tailpos = hostapd_eid_time_adv(hapd, tailpos); + + tailpos = hostapd_eid_interworking(hapd, tailpos); + tailpos = hostapd_eid_adv_proto(hapd, tailpos); + tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -430,19 +548,85 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) } #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { + os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), + wpabuf_len(hapd->p2p_beacon_ie)); + tailpos += wpabuf_len(hapd->p2p_beacon_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + tailpos = hostapd_eid_p2p_manage(hapd, tailpos); +#endif /* CONFIG_P2P_MANAGER */ + tail_len = tailpos > tail ? tailpos - tail : 0; - if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len, - tail, tail_len, hapd->conf->dtim_period, - hapd->iconf->beacon_int)) - wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM " - "period"); +#endif /* NEED_AP_MLME */ + + os_memset(¶ms, 0, sizeof(params)); + params.head = (u8 *) head; + params.head_len = head_len; + params.tail = tail; + params.tail_len = tail_len; + params.dtim_period = hapd->conf->dtim_period; + params.beacon_int = hapd->iconf->beacon_int; + params.ssid = (u8 *) hapd->conf->ssid.ssid; + params.ssid_len = hapd->conf->ssid.ssid_len; + params.pairwise_ciphers = hapd->conf->rsn_pairwise ? + hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; + params.group_cipher = hapd->conf->wpa_group; + params.key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params.auth_algs = hapd->conf->auth_algs; + params.wpa_version = hapd->conf->wpa; + params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)); + switch (hapd->conf->ignore_broadcast_ssid) { + case 0: + params.hide_ssid = NO_SSID_HIDING; + break; + case 1: + params.hide_ssid = HIDDEN_SSID_ZERO_LEN; + break; + case 2: + params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + break; + } + hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp); + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + params.isolate = hapd->conf->isolate; +#ifdef NEED_AP_MLME + params.cts_protect = !!(ieee802_11_erp_info(hapd) & + ERP_INFO_USE_PROTECTION); + params.preamble = hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + params.short_slot_time = + hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; + else + params.short_slot_time = -1; + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) + params.ht_opmode = -1; + else + params.ht_opmode = hapd->iface->ht_op_mode; +#endif /* NEED_AP_MLME */ + params.interworking = hapd->conf->interworking; + if (hapd->conf->interworking && + !is_zero_ether_addr(hapd->conf->hessid)) + params.hessid = hapd->conf->hessid; + params.access_network_type = hapd->conf->access_network_type; + if (hostapd_drv_set_ap(hapd, ¶ms)) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); os_free(tail); os_free(head); - - hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) & - ERP_INFO_USE_PROTECTION)); } diff --git a/src/ap/beacon.h b/src/ap/beacon.h index c1510e1..a944f5f 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -20,17 +20,7 @@ struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len); -#ifdef NEED_AP_MLME void ieee802_11_set_beacon(struct hostapd_data *hapd); void ieee802_11_set_beacons(struct hostapd_iface *iface); -#else /* NEED_AP_MLME */ -static inline void ieee802_11_set_beacon(struct hostapd_data *hapd) -{ -} - -static inline void ieee802_11_set_beacons(struct hostapd_iface *iface) -{ -} -#endif /* NEED_AP_MLME */ #endif /* BEACON_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index e50b0a7..d348dc1 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -21,6 +21,7 @@ #include "ieee802_11.h" #include "sta_info.h" #include "wps_hostapd.h" +#include "p2p_hostapd.h" #include "ctrl_iface_ap.h" @@ -57,6 +58,9 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, buflen - len); if (res >= 0) len += res; + res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; return len; } diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 26ef584..8844132 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -20,6 +20,9 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "crypto/random.h" +#include "p2p/p2p.h" +#include "wps/wps.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" @@ -30,15 +33,19 @@ #include "wpa_auth.h" #include "wmm.h" #include "wps_hostapd.h" +#include "ap_drv_ops.h" #include "ap_config.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ielen) + const u8 *req_ies, size_t req_ies_len, int reassoc) { struct sta_info *sta; int new_assoc, res; struct ieee802_11_elems elems; + const u8 *ie; + size_t ielen; + u16 reason = WLAN_REASON_UNSPECIFIED; if (addr == NULL) { /* @@ -52,11 +59,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, "no address"); return -1; } + random_add_randomness(addr, ETH_ALEN); hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated"); - ieee802_11_parse_elems(ie, ielen, &elems, 0); + ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0); if (elems.wps_ie) { ie = elems.wps_ie - 2; ielen = elems.wps_ie_len + 2; @@ -79,15 +87,30 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta = ap_get_sta(hapd, addr); if (sta) { accounting_sta_stop(hapd, sta); + + /* + * Make sure that the previously registered inactivity timer + * will not remove the STA immediately. + */ + sta->timeout_next = STA_NULLFUNC; } else { sta = ap_sta_add(hapd, addr); if (sta == NULL) return -1; } - sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + P2P_IE_VENDOR_TYPE); + } +#endif /* CONFIG_P2P */ if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { +#ifdef CONFIG_WPS if (hapd->conf->wps_state) { wpa_printf(MSG_DEBUG, "STA did not include " "WPA/RSN IE in (Re)Association " @@ -95,15 +118,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags |= WLAN_STA_MAYBE_WPS; goto skip_wpa_check; } +#endif /* CONFIG_WPS */ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); return -1; } +#ifdef CONFIG_WPS if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + struct wpabuf *wps; sta->flags |= WLAN_STA_WPS; + wps = ieee802_11_vendor_ie_concat(ie, ielen, + WPS_IE_VENDOR_TYPE); + if (wps) { + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA " + "supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } + wpabuf_free(wps); + } goto skip_wpa_check; } +#endif /* CONFIG_WPS */ if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, @@ -116,36 +153,55 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, NULL, 0); if (res != WPA_IE_OK) { - int resp; wpa_printf(MSG_DEBUG, "WPA/RSN information element " "rejected? (res %u)", res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); if (res == WPA_INVALID_GROUP) - resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; + reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; else if (res == WPA_INVALID_AKMP) - resp = WLAN_REASON_AKMP_NOT_VALID; + reason = WLAN_REASON_AKMP_NOT_VALID; #ifdef CONFIG_IEEE80211W else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_REASON_INVALID_IE; + reason = WLAN_REASON_INVALID_IE; else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; #endif /* CONFIG_IEEE80211W */ else - resp = WLAN_REASON_INVALID_IE; - hapd->drv.sta_disassoc(hapd, sta->addr, resp); - ap_free_sta(hapd, sta); - return -1; + reason = WLAN_REASON_INVALID_IE; + goto fail; } } else if (hapd->conf->wps_state) { - if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 && - os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { +#ifdef CONFIG_WPS + struct wpabuf *wps; + if (req_ies) + wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + WPS_IE_VENDOR_TYPE); + else + wps = NULL; +#ifdef CONFIG_WPS_STRICT + if (wps && wps_validate_assoc_req(wps) < 0) { + reason = WLAN_REASON_INVALID_IE; + wpabuf_free(wps); + goto fail; + } +#endif /* CONFIG_WPS_STRICT */ + if (wps) { sta->flags |= WLAN_STA_WPS; + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports " + "WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } } else sta->flags |= WLAN_STA_MAYBE_WPS; + wpabuf_free(wps); +#endif /* CONFIG_WPS */ } +#ifdef CONFIG_WPS skip_wpa_check: +#endif /* CONFIG_WPS */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; @@ -155,7 +211,19 @@ skip_wpa_check: ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_P2P + if (req_ies) { + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, + req_ies, req_ies_len); + } +#endif /* CONFIG_P2P */ + return 0; + +fail: + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); + ap_free_sta(hapd, sta); + return -1; } @@ -163,6 +231,19 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; + if (addr == NULL) { + /* + * This could potentially happen with unexpected event from the + * driver wrapper. This was seen at least in one case where the + * driver ended up reporting a station mode event while hostapd + * was running, so better make sure we stop processing such an + * event here. + */ + wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " + "with no address"); + return; + } + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); @@ -173,9 +254,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) return; } + ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); @@ -183,50 +263,47 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) } -#ifdef HOSTAPD +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); -#ifdef NEED_AP_MLME + if (!sta || !hapd->conf->disassoc_low_ack) + return; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disconnected due to excessive " + "missing ACKs"); + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); + if (sta) + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); +} -static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) + +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len) { - u16 fc, type, stype; + size_t i; + int ret = 0; - /* - * PS-Poll frames are 16 bytes. All other frames are - * 24 bytes or longer. - */ - if (len < 16) - return NULL; + if (sa == NULL || ie == NULL) + return -1; - fc = le_to_host16(hdr->frame_control); - type = WLAN_FC_GET_TYPE(fc); - stype = WLAN_FC_GET_STYPE(fc); - - switch (type) { - case WLAN_FC_TYPE_DATA: - if (len < 24) - return NULL; - switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { - case WLAN_FC_FROMDS | WLAN_FC_TODS: - case WLAN_FC_TODS: - return hdr->addr1; - case WLAN_FC_FROMDS: - return hdr->addr2; - default: - return NULL; + random_add_randomness(sa, ETH_ALEN); + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + sa, da, bssid, ie, ie_len) > 0) { + ret = 1; + break; } - case WLAN_FC_TYPE_CTRL: - if (stype != WLAN_FC_STYPE_PSPOLL) - return NULL; - return hdr->addr1; - case WLAN_FC_TYPE_MGMT: - return hdr->addr3; - default: - return NULL; } + return ret; } +#ifdef HOSTAPD + +#ifdef NEED_AP_MLME + #define HAPD_BROADCAST ((struct hostapd_data *) -1) static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, @@ -250,17 +327,14 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, - const u8 *frame, size_t len) + const u8 *bssid, const u8 *addr, + int wds) { - const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame; - u16 fc = le_to_host16(hdr->frame_control); - hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); + hapd = get_hapd_bssid(hapd->iface, bssid); if (hapd == NULL || hapd == HAPD_BROADCAST) return; - ieee802_11_rx_from_unknown(hapd, hdr->addr2, - (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == - (WLAN_FC_TODS | WLAN_FC_FROMDS)); + ieee802_11_rx_from_unknown(hapd, addr, wds); } @@ -303,6 +377,48 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) rx_mgmt->frame_len, &fi); } else ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); + + random_add_randomness(&fi, sizeof(fi)); +} + + +static void hostapd_rx_action(struct hostapd_data *hapd, + struct rx_action *rx_action) +{ + struct rx_mgmt rx_mgmt; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR + " BSSID=" MACSTR " category=%u", + MAC2STR(rx_action->da), MAC2STR(rx_action->sa), + MAC2STR(rx_action->bssid), rx_action->category); + wpa_hexdump(MSG_MSGDUMP, "Received action frame contents", + rx_action->data, rx_action->len); + + buf = os_zalloc(24 + 1 + rx_action->len); + if (buf == NULL) + return; + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + if (rx_action->category == WLAN_ACTION_SA_QUERY) { + /* + * Assume frame was protected; it would have been dropped if + * not. + */ + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + } + os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN); + os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN); + os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN); + buf[24] = rx_action->category; + os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len); + os_memset(&rx_mgmt, 0, sizeof(rx_mgmt)); + rx_mgmt.frame = buf; + rx_mgmt.frame_len = 24 + 1 + rx_action->len; + hostapd_mgmt_rx(hapd, &rx_mgmt); + os_free(buf); } @@ -320,23 +436,6 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, #endif /* NEED_AP_MLME */ -static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, - const u8 *ie, size_t ie_len) -{ - size_t i; - int ret = 0; - - for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { - if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - sa, ie, ie_len) > 0) { - ret = 1; - break; - } - } - return ret; -} - - static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -395,7 +494,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* CONFIG_IEEE80211R */ case EVENT_WPS_BUTTON_PUSHED: - hostapd_wps_button_pushed(hapd); + hostapd_wps_button_pushed(hapd, NULL); break; #ifdef NEED_AP_MLME case EVENT_TX_STATUS: @@ -414,16 +513,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } break; + case EVENT_DRIVER_CLIENT_POLL_OK: + hostapd_client_poll_ok(hapd, data->client_poll.addr); + break; case EVENT_RX_FROM_UNKNOWN: - hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame, - data->rx_from_unknown.len); + hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid, + data->rx_from_unknown.addr, + data->rx_from_unknown.wds); break; case EVENT_RX_MGMT: hostapd_mgmt_rx(hapd, &data->rx_mgmt); break; #endif /* NEED_AP_MLME */ case EVENT_RX_PROBE_REQ: + if (data->rx_probe_req.sa == NULL || + data->rx_probe_req.ie == NULL) + break; hostapd_probe_req_rx(hapd, data->rx_probe_req.sa, + data->rx_probe_req.da, + data->rx_probe_req.bssid, data->rx_probe_req.ie, data->rx_probe_req.ie_len); break; @@ -438,7 +546,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_ASSOC: hostapd_notif_assoc(hapd, data->assoc_info.addr, data->assoc_info.req_ies, - data->assoc_info.req_ies_len); + data->assoc_info.req_ies_len, + data->assoc_info.reassoc); break; case EVENT_DISASSOC: if (data) @@ -448,6 +557,19 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (data) hostapd_notif_disassoc(hapd, data->deauth_info.addr); break; + case EVENT_STATION_LOW_ACK: + if (!data) + break; + hostapd_event_sta_low_ack(hapd, data->low_ack.addr); + break; +#ifdef NEED_AP_MLME + case EVENT_RX_ACTION: + if (data->rx_action.da == NULL || data->rx_action.sa == NULL || + data->rx_action.bssid == NULL) + break; + hostapd_rx_action(hapd, &data->rx_action); + break; +#endif /* NEED_AP_MLME */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 841f9c5..95aa008 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -35,57 +35,37 @@ #include "wpa_auth_glue.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "p2p_hostapd.h" -static int hostapd_flush_old_stations(struct hostapd_data *hapd); +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); extern int wpa_debug_level; -int hostapd_reload_config(struct hostapd_iface *iface) +static void hostapd_reload_bss(struct hostapd_data *hapd) { - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_config *newconf, *oldconf; - size_t j; - - if (iface->config_read_cb == NULL) - return -1; - newconf = iface->config_read_cb(iface->config_fname); - if (newconf == NULL) - return -1; - - /* - * Deauthenticate all stations since the new configuration may not - * allow them to use the BSS anymore. - */ - for (j = 0; j < iface->num_bss; j++) - hostapd_flush_old_stations(iface->bss[j]); - #ifndef CONFIG_NO_RADIUS - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, etc.) */ - radius_client_flush(hapd->radius, 0); + radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ - oldconf = hapd->iconf; - hapd->iconf = newconf; - hapd->conf = &newconf->bss[0]; - iface->conf = newconf; - if (hostapd_setup_wpa_psk(hapd->conf)) { wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " "after reloading configuration"); } if (hapd->conf->ieee802_1x || hapd->conf->wpa) - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1); else - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); - if (hapd->conf->wpa && hapd->wpa_auth == NULL) + if (hapd->conf->wpa && hapd->wpa_auth == NULL) { hostapd_setup_wpa(hapd); - else if (hapd->conf->wpa) { + if (hapd->wpa_auth) + wpa_init_keys(hapd->wpa_auth); + } else if (hapd->conf->wpa) { const u8 *wpa_ie; size_t wpa_ie_len; hostapd_reconfig_wpa(hapd); @@ -110,10 +90,50 @@ int hostapd_reload_config(struct hostapd_iface *iface) wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); /* try to continue */ } + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); +} + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; + + if (iface->config_read_cb == NULL) + return -1; + newconf = iface->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + /* + * Deauthenticate all stations since the new configuration may not + * allow them to use the BSS anymore. + */ + for (j = 0; j < iface->num_bss; j++) { + hostapd_flush_old_stations(iface->bss[j], + WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_broadcast_wep_clear(iface->bss[j]); + +#ifndef CONFIG_NO_RADIUS + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(iface->bss[j]->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + } + + oldconf = hapd->iconf; + iface->conf = newconf; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + hapd->iconf = newconf; + hapd->conf = &newconf->bss[j]; + hostapd_reload_bss(hapd); + } hostapd_config_free(oldconf); - wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); return 0; } @@ -125,8 +145,8 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, int i; for (i = 0; i < NUM_WEP_KEYS; i++) { - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, - i == 0 ? 1 : 0, NULL, 0, NULL, 0)) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, + 0, NULL, 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear default " "encryption keys (ifname=%s keyidx=%d)", ifname, i); @@ -135,9 +155,9 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W if (hapd->conf->ieee80211w) { for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, - i, i == 0 ? 1 : 0, NULL, 0, - NULL, 0)) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, + NULL, i, 0, NULL, + 0, NULL, 0)) { wpa_printf(MSG_DEBUG, "Failed to clear " "default mgmt encryption keys " "(ifname=%s keyidx=%d)", ifname, i); @@ -162,11 +182,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) idx = ssid->wep.idx; if (ssid->wep.default_len && - hapd->drv.set_key(hapd->conf->iface, - hapd, WPA_ALG_WEP, NULL, idx, - idx == ssid->wep.idx, - NULL, 0, ssid->wep.key[idx], - ssid->wep.len[idx])) { + hostapd_drv_set_key(hapd->conf->iface, + hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, + 1, NULL, 0, ssid->wep.key[idx], + ssid->wep.len[idx])) { wpa_printf(MSG_WARNING, "Could not set WEP encryption."); errors++; } @@ -184,9 +203,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) continue; idx = key->idx; - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, - idx, idx == key->idx, NULL, 0, - key->key[idx], key->len[idx])) { + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, + broadcast_ether_addr, idx, 1, + NULL, 0, key->key[idx], + key->len[idx])) { wpa_printf(MSG_WARNING, "Could not set " "dynamic VLAN WEP encryption."); errors++; @@ -235,6 +255,15 @@ static void hostapd_cleanup(struct hostapd_data *hapd) os_free(hapd->probereq_cb); hapd->probereq_cb = NULL; + +#ifdef CONFIG_P2P + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = NULL; + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = NULL; +#endif /* CONFIG_P2P */ + + wpabuf_free(hapd->time_adv); } @@ -284,12 +313,18 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) return 0; } + /* + * When IEEE 802.1X is not enabled, the driver may need to know how to + * set authentication algorithms for static WEP. + */ + hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs); + for (i = 0; i < 4; i++) { if (hapd->conf->ssid.wep.key[i] && - hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i, - i == hapd->conf->ssid.wep.idx, NULL, 0, - hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i])) { + hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, + i == hapd->conf->ssid.wep.idx, NULL, 0, + hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i])) { wpa_printf(MSG_WARNING, "Could not set WEP " "encryption."); return -1; @@ -303,9 +338,10 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) } -static int hostapd_flush_old_stations(struct hostapd_data *hapd) +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) { int ret = 0; + u8 addr[ETH_ALEN]; if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; @@ -316,17 +352,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd) ret = -1; } wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); - - /* New Prism2.5/3 STA firmware versions seem to have issues with this - * broadcast deauth frame. This gets the firmware in odd state where - * nothing works correctly, so let's skip sending this for the hostap - * driver. */ - if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { - u8 addr[ETH_ALEN]; - os_memset(addr, 0xff, ETH_ALEN); - hapd->drv.sta_deauth(hapd, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - } + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + hostapd_free_stas(hapd); return ret; } @@ -344,7 +372,6 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) u8 mask[ETH_ALEN] = { 0 }; struct hostapd_data *hapd = iface->bss[0]; unsigned int i = iface->conf->num_bss, bits = 0, j; - int res; int auto_addr = 0; if (hostapd_drv_none(hapd)) @@ -408,17 +435,6 @@ skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); - res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); - if (res == 0) - return 0; - - if (res < 0) { - wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " - MACSTR " for start address " MACSTR ".", - MAC2STR(mask), MAC2STR(hapd->own_addr)); - return -1; - } - if (!auto_addr) return 0; @@ -495,14 +511,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, hapd->conf->iface, hapd->own_addr, hapd, - &hapd->drv_priv, force_ifname, if_addr)) { + &hapd->drv_priv, force_ifname, if_addr, + hapd->conf->bridge[0] ? hapd->conf->bridge : + NULL)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); return -1; } } - hostapd_flush_old_stations(hapd); + if (conf->wmm_enabled < 0) + conf->wmm_enabled = hapd->iconf->ieee80211n; + + hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); @@ -611,6 +632,12 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) ieee802_11_set_beacon(hapd); + if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) + return -1; + + if (hapd->driver && hapd->driver->set_operstate) + hapd->driver->set_operstate(hapd->drv_priv, 1); + return 0; } @@ -624,9 +651,6 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) for (i = 0; i < NUM_TX_QUEUES; i++) { p = &iface->conf->tx_queue[i]; - if (!p->configured) - continue; - if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, p->cwmax, p->burst)) { wpa_printf(MSG_DEBUG, "Failed to set TX queue " @@ -717,6 +741,17 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) } } + if (iface->current_mode) { + if (hostapd_prepare_rates(hapd, iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates " + "table."); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + } + if (hapd->iconf->rts_threshold > -1 && hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " @@ -753,6 +788,20 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) return -1; } + /* + * WPS UPnP module can be initialized only when the "upnp_iface" is up. + * If "interface" and "upnp_iface" are the same (e.g., non-bridge + * mode), the interface is up only after driver_commit, so initialize + * WPS after driver_commit. + */ + for (j = 0; j < iface->num_bss; j++) { + if (hostapd_init_wps_complete(iface->bss[j])) + return -1; + } + + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); @@ -807,7 +856,6 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, if (hapd == NULL) return NULL; - hostapd_set_driver_ops(&hapd->drv); hapd->new_assoc_sta_cb = hostapd_new_assoc_sta; hapd->iconf = conf; hapd->conf = bss; @@ -829,7 +877,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); hostapd_cleanup(hapd); } } @@ -859,8 +907,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc) { if (hapd->tkip_countermeasures) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); return; } @@ -870,6 +918,15 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->ieee802_11f) iapp_new_station(hapd->iapp, sta); +#ifdef CONFIG_P2P + if (sta->p2p_ie == NULL && !sta->no_p2p_set) { + sta->no_p2p_set = 1; + hapd->num_sta_no_p2p++; + if (hapd->num_sta_no_p2p == 1) + hostapd_p2p_non_p2p_sta_connected(hapd); + } +#endif /* CONFIG_P2P */ + /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d0d67c8..72cf012 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -27,9 +27,12 @@ struct sta_info; struct hostap_sta_driver_data; struct ieee80211_ht_capabilities; struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; struct hostapd_probereq_cb { - int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len); + int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len); void *ctx; }; @@ -47,55 +50,6 @@ struct hostapd_frame_info { }; -struct hostapd_driver_ops { - int (*set_ap_wps_ie)(struct hostapd_data *hapd); - int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg, - size_t len); - int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr, - const u8 *data, size_t data_len, int encrypt); - int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta, - int authorized); - int (*set_key)(const char *ifname, struct hostapd_data *hapd, - enum wpa_alg alg, const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len); - int (*read_sta_data)(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, - const u8 *addr); - int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr); - int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta); - int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname, - int enabled); - int (*set_radius_acl_auth)(struct hostapd_data *hapd, - const u8 *mac, int accepted, - u32 session_timeout); - int (*set_radius_acl_expire)(struct hostapd_data *hapd, - const u8 *mac); - int (*set_bss_params)(struct hostapd_data *hapd, int use_protection); - int (*set_beacon)(struct hostapd_data *hapd, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int); - int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname); - int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname); - int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid, - int val); - int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int vlan_id); - int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr); - int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr, - int reason); - int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr, - int reason); - int (*sta_add)(struct hostapd_data *hapd, - const u8 *addr, u16 aid, u16 capability, - const u8 *supp_rates, size_t supp_rates_len, - u16 listen_interval, - const struct ieee80211_ht_capabilities *ht_capab); - int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr); - int (*set_countermeasures)(struct hostapd_data *hapd, int enabled); -}; - /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -123,12 +77,12 @@ struct hostapd_data { const struct wpa_driver_ops *driver; void *drv_priv; - struct hostapd_driver_ops drv; void (*new_assoc_sta_cb)(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); void *msg_ctx; /* ctx for wpa_msg() calls */ + void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; u32 acct_session_id_hi, acct_session_id_lo; @@ -155,6 +109,10 @@ struct hostapd_data { int parameter_set_count; + /* Time Advertisement */ + u8 time_update_counter; + struct wpabuf *time_adv; + #ifdef CONFIG_FULL_DYNAMIC_VLAN struct full_dynamic_vlan *full_dynamic_vlan; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -162,10 +120,12 @@ struct hostapd_data { struct l2_packet_data *l2; struct wps_context *wps; + int beacon_set_done; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; #ifdef CONFIG_WPS unsigned int ap_pin_failures; + unsigned int ap_pin_failures_consecutive; struct upnp_wps_device_sm *wps_upnp; unsigned int ap_pin_lockout_time; #endif /* CONFIG_WPS */ @@ -177,9 +137,39 @@ struct hostapd_data { int freq); void *public_action_cb_ctx; + int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *vendor_action_cb_ctx; + void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); void *wps_reg_success_cb_ctx; + + void (*wps_event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); + void *wps_event_cb_ctx; + + void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, + int authorized, const u8 *p2p_dev_addr); + void *sta_authorized_cb_ctx; + + void (*setup_complete_cb)(void *ctx); + void *setup_complete_cb_ctx; + +#ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; + struct wpabuf *p2p_beacon_ie; + struct wpabuf *p2p_probe_resp_ie; + + /* Number of non-P2P association stations */ + int num_sta_no_p2p; + + /* Periodic NoA (used only when no non-P2P clients in the group) */ + int noa_enabled; + int noa_start; + int noa_duration; +#endif /* CONFIG_P2P */ }; @@ -202,6 +192,7 @@ struct hostapd_iface { struct ap_info *ap_hash[STA_HASH_SIZE]; struct ap_info *ap_iter_list; + unsigned int drv_flags; struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; @@ -264,13 +255,17 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, + const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len), void *ctx); void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ielen); + const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len); #endif /* HOSTAPD_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 0159c72..e723543 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -101,8 +101,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) } -static int hostapd_prepare_rates(struct hostapd_data *hapd, - struct hostapd_hw_modes *mode) +int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode) { int i, num_basic_rates = 0; int basic_rates_a[] = { 60, 120, 240, -1 }; @@ -162,7 +162,8 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd, hapd->iface->num_rates++; } - if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { + if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) && + (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) { wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " "rate sets (%d,%d).", hapd->iface->num_rates, num_basic_rates); @@ -265,11 +266,11 @@ static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, oper = (struct ieee80211_ht_operation *) elems.ht_operation; *pri_chan = oper->control_chan; if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { - if (oper->ht_param & - HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + int sec = oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) *sec_chan = *pri_chan + 4; - else if (oper->ht_param & - HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) *sec_chan = *pri_chan - 4; } } @@ -406,24 +407,11 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, } -static void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - static void ieee80211n_check_scan(struct hostapd_iface *iface) { struct wpa_scan_results *scan_res; int oper40; + int res; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ @@ -452,7 +440,8 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; } - hostapd_setup_interface_complete(iface, 0); + res = ieee80211n_allowed_ht40_channel_pair(iface); + hostapd_setup_interface_complete(iface, !res); } @@ -483,9 +472,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) u16 hw = iface->current_mode->ht_capab; u16 conf = iface->conf->ht_capab; - if (!iface->conf->ieee80211n) - return 1; - if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { wpa_printf(MSG_ERROR, "Driver does not support configured " @@ -585,13 +571,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) { #ifdef CONFIG_IEEE80211N int ret; + if (!iface->conf->ieee80211n) + return 0; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; ret = ieee80211n_check_40mhz(iface); if (ret) return ret; if (!ieee80211n_allowed_ht40_channel_pair(iface)) return -1; - if (!ieee80211n_supported_ht_capab(iface)) - return -1; #endif /* CONFIG_IEEE80211N */ return 0; @@ -601,7 +589,7 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. - * Returns: 0 on success, -1 on failure + * Returns: 0 on success, < 0 on failure * * Sets up the hardware mode, channel, rates, and passive scanning * based on the configuration. @@ -628,18 +616,51 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured mode " - "(%d)", (int) iface->conf->hw_mode); - return -1; + "(%d) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; } ok = 0; for (j = 0; j < iface->current_mode->num_channels; j++) { struct hostapd_channel_data *chan = &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - (chan->chan == iface->conf->channel)) { - ok = 1; - break; + if (chan->chan == iface->conf->channel) { + if (chan->flag & HOSTAPD_CHAN_DISABLED) { + wpa_printf(MSG_ERROR, + "channel [%i] (%i) is disabled for " + "use in AP mode, flags: 0x%x", + j, chan->chan, chan->flag); + } else { + ok = 1; + break; + } + } + } + if (ok && iface->conf->secondary_channel) { + int sec_ok = 0; + int sec_chan = iface->conf->channel + + iface->conf->secondary_channel * 4; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->chan == sec_chan)) { + sec_ok = 1; + break; + } + } + if (!sec_ok) { + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured HT40 secondary channel " + "(%d) not found from the channel list " + "of current mode (%d) %s", + sec_chan, iface->current_mode->mode, + hostapd_hw_mode_txt( + iface->current_mode->mode)); + ok = 0; } } if (iface->conf->channel == 0) { @@ -647,7 +668,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) * the channel automatically */ wpa_printf(MSG_ERROR, "Channel not configured " "(hw_mode/channel in hostapd.conf)"); - return -1; + return -3; } if (ok == 0 && iface->conf->channel != 0) { hostapd_logger(iface->bss[0], NULL, @@ -665,15 +686,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Hardware does not support configured channel"); - return -1; - } - - if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { - wpa_printf(MSG_ERROR, "Failed to prepare rates table."); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Failed to prepare rates table."); - return -1; + return -4; } return 0; diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index 0295549..b84bca6 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -25,6 +25,8 @@ const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); +int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -39,7 +41,7 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) { - return -1; + return -100; } static inline const char * hostapd_hw_mode_txt(int mode) @@ -57,6 +59,12 @@ static inline int hostapd_check_ht_capab(struct hostapd_iface *iface) return 0; } +static inline int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode) +{ + return 0; +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 3375aa2..d30d5ae 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2010, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -25,6 +25,7 @@ #include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" +#include "p2p/p2p.h" #include "wps/wps.h" #include "hostapd.h" #include "beacon.h" @@ -37,6 +38,8 @@ #include "accounting.h" #include "ap_config.h" #include "ap_mlme.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" #include "ieee802_11.h" @@ -50,6 +53,8 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = WLAN_EID_SUPP_RATES; num = hapd->iface->num_rates; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ @@ -67,6 +72,10 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && + hapd->iface->num_rates < 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + return pos; } @@ -80,6 +89,8 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) return eid; num = hapd->iface->num_rates; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; if (num <= 8) return eid; num -= 8; @@ -98,6 +109,10 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) pos++; } + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && + hapd->iface->num_rates >= 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + return pos; } @@ -148,34 +163,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, } -#ifdef CONFIG_IEEE80211W -static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, - struct sta_info *sta, u8 *eid) -{ - u8 *pos = eid; - u32 timeout, tu; - struct os_time now, passed; - - *pos++ = WLAN_EID_TIMEOUT_INTERVAL; - *pos++ = 5; - *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); - tu = (passed.sec * 1000000 + passed.usec) / 1024; - if (hapd->conf->assoc_sa_query_max_timeout > tu) - timeout = hapd->conf->assoc_sa_query_max_timeout - tu; - else - timeout = 0; - if (timeout < hapd->conf->assoc_sa_query_max_timeout) - timeout++; /* add some extra time for local timers */ - WPA_PUT_LE32(pos, timeout); - pos += 4; - - return pos; -} -#endif /* CONFIG_IEEE80211W */ - - void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) { int i; @@ -204,15 +191,15 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; - time_t now; + struct os_time now; int r; sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - now = time(NULL); - r = random(); - os_memcpy(key, &now, 4); + os_get_time(&now); + r = os_random(); + os_memcpy(key, &now.sec, 4); os_memcpy(key + 4, &r, 4); rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); @@ -282,7 +269,7 @@ static void send_auth_reply(struct hostapd_data *hapd, " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); - if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0) + if (hostapd_drv_send_mlme(hapd, reply, rlen) < 0) perror("send_auth_reply: send"); os_free(buf); @@ -553,7 +540,7 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, { sta->flags &= ~WLAN_STA_WMM; if (wmm_ie && hapd->conf->wmm_enabled) { - if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) + if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, @@ -635,10 +622,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211N - resp = copy_sta_ht_capab(sta, elems.ht_capabilities, + resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities, elems.ht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && + !(sta->flags & WLAN_STA_HT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory HT PHY - reject association"); + return WLAN_STATUS_ASSOC_DENIED_NO_HT; + } #endif /* CONFIG_IEEE80211N */ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { @@ -654,7 +648,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #ifdef CONFIG_WPS - sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); if (hapd->conf->wps_state && elems.wps_ie) { wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " "Request - assume WPS is used"); @@ -662,8 +656,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpabuf_free(sta->wps_ie); sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_IE_VENDOR_TYPE); + if (sta->wps_ie && wps_is_20(sta->wps_ie)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } wpa_ie = NULL; wpa_ie_len = 0; + if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " + "(Re)Association Request - reject"); + return WLAN_STATUS_INVALID_IE; + } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " "(Re)Association Request - possible WPS use"); @@ -768,6 +771,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } else wpa_auth_sta_no_wpa(sta->wpa_sm); +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + + } else { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = NULL; + } + + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); +#endif /* CONFIG_P2P */ + return WLAN_STATUS_SUCCESS; } @@ -788,7 +805,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); - if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, &reply, send_len) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } @@ -845,11 +862,14 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ + p = hostapd_eid_ext_capab(hapd, p); + if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS - if (sta->flags & WLAN_STA_WPS) { + if ((sta->flags & WLAN_STA_WPS) || + ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); @@ -859,9 +879,39 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (sta->p2p_ie) { + struct wpabuf *p2p_resp_ie; + enum p2p_status_code status; + switch (status_code) { + case WLAN_STATUS_SUCCESS: + status = P2P_SC_SUCCESS; + break; + case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + status = P2P_SC_FAIL_LIMIT_REACHED; + break; + default: + status = P2P_SC_FAIL_INVALID_PARAMS; + break; + } + p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); + if (p2p_resp_ie) { + os_memcpy(p, wpabuf_head(p2p_resp_ie), + wpabuf_len(p2p_resp_ie)); + p += wpabuf_len(p2p_resp_ie); + wpabuf_free(p2p_resp_ie); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) + p = hostapd_eid_p2p_manage(hapd, p); +#endif /* CONFIG_P2P_MANAGER */ + send_len += p - reply->u.assoc_resp.variable; - if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, reply, send_len) < 0) wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); } @@ -1005,6 +1055,7 @@ static void handle_assoc(struct hostapd_data *hapd, "association OK (aid %d)", sta->aid); /* Station will be marked associated, after it acknowledges AssocResp */ + sta->flags |= WLAN_STA_ASSOC_REQ_OK; #ifdef CONFIG_IEEE80211W if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { @@ -1061,9 +1112,8 @@ static void handle_disassoc(struct hostapd_data *hapd, return; } - sta->flags &= ~WLAN_STA_ASSOC; - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); @@ -1073,7 +1123,7 @@ static void handle_disassoc(struct hostapd_data *hapd, * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { @@ -1094,26 +1144,26 @@ static void handle_deauth(struct hostapd_data *hapd, struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { - printf("handle_deauth - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " + "payload (len=%lu)", (unsigned long) len); return; } - wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR - " reason_code=%d", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.deauth.reason_code)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR + " reason_code=%d", + MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code)); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { - printf("Station " MACSTR " trying to deauthenticate, but it " - "is not authenticated.\n", MAC2STR(mgmt->sa)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " + "to deauthenticate, but it is not authenticated", + MAC2STR(mgmt->sa)); return; } - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); @@ -1148,81 +1198,11 @@ static void handle_beacon(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W -/* MLME-SAQuery.request */ -void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, - const u8 *addr, const u8 *trans_id) -{ - struct ieee80211_mgmt mgmt; - u8 *end; - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " - MACSTR, MAC2STR(addr)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - trans_id, WLAN_SA_QUERY_TR_ID_LEN); - - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.action.category = WLAN_ACTION_SA_QUERY; - mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; - os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0) - perror("ieee802_11_send_sa_query_req: send"); -} - - -static void hostapd_sa_query_request(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt) -{ - struct sta_info *sta; - struct ieee80211_mgmt resp; - u8 *end; - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " - MACSTR, MAC2STR(mgmt->sa)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " - "from unassociated STA " MACSTR, MAC2STR(mgmt->sa)); - return; - } - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " - MACSTR, MAC2STR(mgmt->sa)); - - os_memset(&resp, 0, sizeof(resp)); - resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(resp.da, mgmt->sa, ETH_ALEN); - os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); - resp.u.action.category = WLAN_ACTION_SA_QUERY; - resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(resp.u.action.u.sa_query_req.trans_id, - mgmt->u.action.u.sa_query_req.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0) - perror("hostapd_sa_query_request: send"); -} - - static void hostapd_sa_query_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { - struct sta_info *sta; const u8 *end; - int i; end = mgmt->u.action.u.sa_query_resp.trans_id + WLAN_SA_QUERY_TR_ID_LEN; @@ -1232,50 +1212,9 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd, return; } - if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) { - hostapd_sa_query_request(hapd, mgmt); - return; - } - - if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " - "Action %d", mgmt->u.action.u.sa_query_resp.action); - return; - } - - wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " - MACSTR, MAC2STR(mgmt->sa)); - wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - - /* MLME-SAQuery.confirm */ - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL || sta->sa_query_trans_id == NULL) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " - "pending SA Query request found"); - return; - } - - for (i = 0; i < sta->sa_query_count; i++) { - if (os_memcmp(sta->sa_query_trans_id + - i * WLAN_SA_QUERY_TR_ID_LEN, - mgmt->u.action.u.sa_query_resp.trans_id, - WLAN_SA_QUERY_TR_ID_LEN) == 0) - break; - } - - if (i >= sta->sa_query_count) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " - "transaction identifier found"); - return; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Reply to pending SA Query received"); - ap_sta_stop_sa_query(hapd, sta); + ieee802_11_sa_query_action(hapd, mgmt->sa, + mgmt->u.action.u.sa_query_resp.action, + mgmt->u.action.u.sa_query_resp.trans_id); } @@ -1290,7 +1229,10 @@ static int robust_action_frame(u8 category) static void handle_action(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) struct sta_info *sta; + sta = ap_get_sta(hapd, mgmt->sa); +#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1300,7 +1242,6 @@ static void handle_action(struct hostapd_data *hapd, return; } - sta = ap_get_sta(hapd, mgmt->sa); #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && @@ -1347,6 +1288,14 @@ static void handle_action(struct hostapd_data *hapd, return; } break; + case WLAN_ACTION_VENDOR_SPECIFIC: + if (hapd->vendor_action_cb) { + if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, + (u8 *) mgmt, len, + hapd->iface->freq) == 0) + return; + } + break; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1374,7 +1323,7 @@ static void handle_action(struct hostapd_data *hapd, os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); resp->u.action.category |= 0x80; - hapd->drv.send_mgmt_frame(hapd, resp, len); + hostapd_drv_send_mlme(hapd, resp, len); os_free(resp); } } @@ -1400,6 +1349,9 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, int broadcast; u16 fc, stype; + if (len < 24) + return; + mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); @@ -1414,6 +1366,11 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; if (!broadcast && +#ifdef CONFIG_P2P + /* Invitation responses can be sent with the peer MAC as BSSID */ + !((hapd->conf->p2p & P2P_GROUP_OWNER) && + stype == WLAN_FC_STYPE_ACTION) && +#endif /* CONFIG_P2P */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { printf("MGMT: BSSID=" MACSTR " not our address\n", MAC2STR(mgmt->bssid)); @@ -1452,7 +1409,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_disassoc(hapd, mgmt, len); break; case WLAN_FC_STYPE_DEAUTH: - wpa_printf(MSG_DEBUG, "mgmt::deauth"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); break; case WLAN_FC_STYPE_ACTION: @@ -1565,9 +1522,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * Open, static WEP, or FT protocol; no separate authorization * step. */ - sta->flags |= WLAN_STA_AUTHORIZED; - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + ap_sta_set_authorized(hapd, sta, 1); } if (reassoc) @@ -1584,22 +1539,31 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * cleared and configuration gets updated in case of reassociation back * to the same AP. */ - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); #ifdef CONFIG_IEEE80211N if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #endif /* CONFIG_IEEE80211N */ - if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->supported_rates, sta->supported_rates_len, - sta->listen_interval, - sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) { + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); + + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_DISASSOC_AP_BUSY); + + goto fail; } + if (sta->flags & WLAN_STA_WDS) + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, @@ -1614,7 +1578,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, goto fail; } - hapd->drv.set_sta_flags(hapd, sta); + hostapd_set_sta_flags(hapd, sta); if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); @@ -1633,6 +1597,54 @@ static void handle_assoc_cb(struct hostapd_data *hapd, } +static void handle_deauth_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "deauth", MAC2STR(sta->addr)); + + ap_sta_deauth_cb(hapd, sta); +} + + +static void handle_disassoc_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "disassoc", MAC2STR(sta->addr)); + + ap_sta_disassoc_cb(hapd, sta); +} + + /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame @@ -1662,10 +1674,15 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); + wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); break; case WLAN_FC_STYPE_DEAUTH: - /* ignore */ + wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); + handle_deauth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); + handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb"); @@ -1708,7 +1725,7 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, break; } } - if (sta == NULL) + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) return; if (sta->flags & WLAN_STA_PENDING_POLL) { wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " @@ -1722,6 +1739,32 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, } +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL && iface->num_bss > 1) { + size_t j; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, addr); + if (sta) + break; + } + } + if (sta == NULL) + return; + if (!(sta->flags & WLAN_STA_PENDING_POLL)) + return; + + wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " + "activity poll", MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_PENDING_POLL; +} + + void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds) { @@ -1734,19 +1777,32 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; - hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1); + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); } return; } wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); + if (src[0] & 0x01) { + /* Broadcast bit set in SA?! Ignore the frame silently. */ + return; + } + + if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { + wpa_printf(MSG_DEBUG, "Association Response to the STA has " + "already been sent, but no TX status yet known - " + "ignore Class 3 frame issue with " MACSTR, + MAC2STR(src)); + return; + } + if (sta && (sta->flags & WLAN_STA_AUTH)) - hapd->drv.sta_disassoc( + hostapd_drv_sta_disassoc( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); else - hapd->drv.sta_deauth( + hostapd_drv_sta_deauth( hapd, src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index cfc069c..b358060 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -46,6 +46,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); @@ -56,12 +57,24 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap); -u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, - size_t ht_capab_len); +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds); +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid); +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const u8 *sa, const u8 action_type, + const u8 *trans_id); +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); +int hostapd_update_time_adv(struct hostapd_data *hapd); +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index dec56d1..88f97b7 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -25,6 +25,7 @@ #include "radius/radius_client.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "ieee802_11.h" #include "ieee802_11_auth.h" @@ -32,7 +33,7 @@ struct hostapd_cached_radius_acl { - time_t timestamp; + os_time_t timestamp; macaddr addr; int accepted; /* HOSTAPD_ACL_* */ struct hostapd_cached_radius_acl *next; @@ -43,7 +44,7 @@ struct hostapd_cached_radius_acl { struct hostapd_acl_query_data { - time_t timestamp; + os_time_t timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ @@ -70,14 +71,14 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *acct_interim_interval, int *vlan_id) { struct hostapd_cached_radius_acl *entry; - time_t now; + struct os_time now; - time(&now); + os_get_time(&now); entry = hapd->acl_cache; while (entry) { if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) + if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) return -1; /* entry has expired */ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) if (session_timeout) @@ -190,7 +191,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) + goto fail; return 0; fail: @@ -240,6 +242,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; + struct os_time t; /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, @@ -270,7 +273,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } - time(&query->timestamp); + os_get_time(&t); + query->timestamp = t.sec; os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { wpa_printf(MSG_DEBUG, "Failed to send Access-Request " @@ -302,7 +306,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, #ifndef CONFIG_NO_RADIUS -static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -317,9 +321,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) prev->next = entry->next; else hapd->acl_cache = entry->next; -#ifdef CONFIG_DRIVER_RADIUS_ACL - hapd->drv.set_radius_acl_expire(hapd, entry->addr); -#endif /* CONFIG_DRIVER_RADIUS_ACL */ + hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; os_free(tmp); @@ -332,7 +334,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) } -static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, + os_time_t now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -368,11 +371,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; - time_t now; + struct os_time now; - time(&now); - hostapd_acl_expire_cache(hapd, now); - hostapd_acl_expire_queries(hapd, now); + os_get_time(&now); + hostapd_acl_expire_cache(hapd, now.sec); + hostapd_acl_expire_queries(hapd, now.sec); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } @@ -397,6 +400,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); + struct os_time t; query = hapd->acl_queries; prev = NULL; @@ -431,7 +435,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } - time(&cache->timestamp); + os_get_time(&t); + cache->timestamp = t.sec; os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, @@ -458,8 +463,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, hapd->acl_cache = cache; #ifdef CONFIG_DRIVER_RADIUS_ACL - hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted, - cache->session_timeout); + hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); #else /* CONFIG_DRIVER_RADIUS_ACL */ #ifdef NEED_AP_MLME /* Re-send original authentication frame for 802.11 processing */ diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 7541b83..6c3696f 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -30,7 +30,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_capabilities *cap; u8 *pos = eid; - if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode) + if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || + hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_CAP; @@ -58,7 +59,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211n) + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) return eid; *pos++ = WLAN_EID_HT_OPERATION; @@ -92,7 +93,6 @@ Set to 1 (HT non-member protection) if there may be non-HT STAs Set to 2 if only HT STAs are associated in BSS, however and at least one 20 MHz HT STA is associated Set to 3 (HT mixed mode) when one or more non-HT STAs are associated - (currently non-GF HT station is considered as non-HT STA also) */ int hostapd_ht_operation_update(struct hostapd_iface *iface) { @@ -130,13 +130,8 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) op_mode_changes++; } - /* Note: currently we switch to the MIXED op mode if HT non-greenfield - * station is associated. Probably it's a theoretical case, since - * it looks like all known HT STAs support greenfield. - */ new_op_mode = 0; - if (iface->num_sta_no_ht || - (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + if (iface->num_sta_no_ht) new_op_mode = OP_MODE_MIXED; else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && iface->num_sta_ht_20mhz) @@ -160,11 +155,13 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } -u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, - size_t ht_capab_len) +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab, size_t ht_capab_len) { + /* Disable HT caps for STAs associated to no-HT BSSes. */ if (!ht_capab || - ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) { + ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || + hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; @@ -253,8 +250,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, return; os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); cap = le_to_host16(neg_ht_cap->ht_capabilities_info); - cap &= hapd->iconf->ht_capab; - cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); + + /* + * Mask out HT features we don't support, but don't overwrite + * non-symmetric features like STBC and SMPS. Just because + * we're not in dynamic SMPS mode the STA might still be. + */ + cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); /* * STBC needs to be handled specially diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c new file mode 100644 index 0000000..c447bce --- /dev/null +++ b/src/ap/ieee802_11_shared.c @@ -0,0 +1,405 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2010, Jouni Malinen + * + * 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 "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "ieee802_11.h" + + +#ifdef CONFIG_IEEE80211W + +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_time now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; + + return pos; +} + + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " + MACSTR, MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0) + perror("ieee802_11_send_sa_query_req: send"); +} + + +void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, + const u8 *sa, const u8 *trans_id) +{ + struct sta_info *sta; + struct ieee80211_mgmt resp; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " + "from unassociated STA " MACSTR, MAC2STR(sa)); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(sa)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp.da, sa, ETH_ALEN); + os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); + resp.u.action.category = WLAN_ACTION_SA_QUERY; + resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0) + perror("ieee80211_mgmt_sa_query_request: send"); +} + + +void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, + const u8 action_type, const u8 *trans_id) +{ + struct sta_info *sta; + int i; + + if (action_type == WLAN_SA_QUERY_REQUEST) { + ieee802_11_send_sa_query_resp(hapd, sa, trans_id); + return; + } + + if (action_type != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", action_type); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); + return; + } + + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} + +#endif /* CONFIG_IEEE80211W */ + + +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = 0; + + if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) + len = 5; + if (len < 4 && hapd->conf->interworking) + len = 4; + if (len == 0) + return eid; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = len; + *pos++ = 0x00; + *pos++ = 0x00; + *pos++ = 0x00; + + *pos = 0x00; + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ + pos++; + + if (len < 5) + return pos; + *pos = 0x00; + if (hapd->conf->tdls & TDLS_PROHIBIT) + *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ + if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) + *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */ + pos++; + + return pos; +} + + +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_INTERWORKING; + len = pos++; + + *pos = hapd->conf->access_network_type; + if (hapd->conf->internet) + *pos |= INTERWORKING_ANO_INTERNET; + if (hapd->conf->asra) + *pos |= INTERWORKING_ANO_ASRA; + if (hapd->conf->esr) + *pos |= INTERWORKING_ANO_ESR; + if (hapd->conf->uesa) + *pos |= INTERWORKING_ANO_UESA; + pos++; + + if (hapd->conf->venue_info_set) { + *pos++ = hapd->conf->venue_group; + *pos++ = hapd->conf->venue_type; + } + + if (!is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + + /* TODO: Separate configuration for ANQP? */ + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_ADV_PROTO; + *pos++ = 2; + *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ + *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + unsigned int i, count; + + if (!hapd->conf->interworking || + hapd->conf->roaming_consortium == NULL || + hapd->conf->roaming_consortium_count == 0) + return eid; + + *pos++ = WLAN_EID_ROAMING_CONSORTIUM; + len = pos++; + + /* Number of ANQP OIs (in addition to the max 3 listed here) */ + if (hapd->conf->roaming_consortium_count > 3 + 255) + *pos++ = 255; + else if (hapd->conf->roaming_consortium_count > 3) + *pos++ = hapd->conf->roaming_consortium_count - 3; + else + *pos++ = 0; + + /* OU #1 and #2 Lengths */ + *pos = hapd->conf->roaming_consortium[0].len; + if (hapd->conf->roaming_consortium_count > 1) + *pos |= hapd->conf->roaming_consortium[1].len << 4; + pos++; + + if (hapd->conf->roaming_consortium_count > 3) + count = 3; + else + count = hapd->conf->roaming_consortium_count; + + for (i = 0; i < count; i++) { + os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, + hapd->conf->roaming_consortium[i].len); + pos += hapd->conf->roaming_consortium[i].len; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->conf->time_advertisement != 2) + return eid; + + if (hapd->time_adv == NULL && + hostapd_update_time_adv(hapd) < 0) + return eid; + + if (hapd->time_adv == NULL) + return eid; + + os_memcpy(eid, wpabuf_head(hapd->time_adv), + wpabuf_len(hapd->time_adv)); + eid += wpabuf_len(hapd->time_adv); + + return eid; +} + + +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) +{ + size_t len; + + if (hapd->conf->time_advertisement != 2) + return eid; + + len = os_strlen(hapd->conf->time_zone); + + *eid++ = WLAN_EID_TIME_ZONE; + *eid++ = len; + os_memcpy(eid, hapd->conf->time_zone, len); + eid += len; + + return eid; +} + + +int hostapd_update_time_adv(struct hostapd_data *hapd) +{ + const int elen = 2 + 1 + 10 + 5 + 1; + struct os_time t; + struct os_tm tm; + u8 *pos; + + if (hapd->conf->time_advertisement != 2) + return 0; + + if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) + return -1; + + if (!hapd->time_adv) { + hapd->time_adv = wpabuf_alloc(elen); + if (hapd->time_adv == NULL) + return -1; + pos = wpabuf_put(hapd->time_adv, elen); + } else + pos = wpabuf_mhead_u8(hapd->time_adv); + + *pos++ = WLAN_EID_TIME_ADVERTISEMENT; + *pos++ = 1 + 10 + 5 + 1; + + *pos++ = 2; /* UTC time at which the TSF timer is 0 */ + + /* Time Value at TSF 0 */ + /* FIX: need to calculate this based on the current TSF value */ + WPA_PUT_LE16(pos, tm.year); /* Year */ + pos += 2; + *pos++ = tm.month; /* Month */ + *pos++ = tm.day; /* Day of month */ + *pos++ = tm.hour; /* Hours */ + *pos++ = tm.min; /* Minutes */ + *pos++ = tm.sec; /* Seconds */ + WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ + pos += 2; + *pos++ = 0; /* Reserved */ + + /* Time Error */ + /* TODO: fill in an estimate on the error */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + + *pos++ = hapd->time_update_counter++; + + return 0; +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index eb160f8..f9a0cbb 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * * 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 @@ -18,6 +18,7 @@ #include "utils/eloop.h" #include "crypto/md5.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "radius/radius.h" @@ -26,6 +27,7 @@ #include "eap_common/eap_wsc_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "sta_info.h" @@ -33,6 +35,7 @@ #include "preauth_auth.h" #include "pmksa_cache_auth.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "ieee802_1x.h" @@ -70,7 +73,8 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt); + hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len, + encrypt, sta->flags); } os_free(buf); @@ -86,21 +90,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, return; if (authorized) { - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); - sta->flags |= WLAN_STA_AUTHORIZED; - res = hapd->drv.set_authorized(hapd, sta, 1); + ap_sta_set_authorized(hapd, sta, 1); + res = hostapd_set_authorized(hapd, sta, 1); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { - if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) == - (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) - wpa_msg(hapd->msg_ctx, MSG_INFO, - AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_AUTHORIZED; - res = hapd->drv.set_authorized(hapd, sta, 0); + ap_sta_set_authorized(hapd, sta, 0); + res = hostapd_set_authorized(hapd, sta, 0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); } @@ -140,7 +136,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, key->key_length = htons(key_len); wpa_get_ntp_timestamp(key->replay_counter); - if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); os_free(buf); return; @@ -215,7 +211,7 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) if (!key->key[key->idx]) key->key[key->idx] = os_malloc(key->default_len); if (key->key[key->idx] == NULL || - os_get_random(key->key[key->idx], key->default_len)) { + random_get_bytes(key->key[key->idx], key->default_len)) { printf("Could not generate random WEP key (dynamic VLAN).\n"); os_free(key->key[key->idx]); key->key[key->idx] = NULL; @@ -229,11 +225,13 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", key->key[key->idx], key->len[key->idx]); - if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1, - NULL, 0, key->key[key->idx], key->len[key->idx])) + if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, + broadcast_ether_addr, key->idx, 1, + NULL, 0, key->key[key->idx], + key->len[key->idx])) printf("Could not set dynamic VLAN WEP encryption key.\n"); - hapd->drv.set_drv_ieee8021x(hapd, ifname, 1); + hostapd_set_drv_ieee8021x(hapd, ifname, 1); return key; } @@ -330,7 +328,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) u8 *ikey; ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || - os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) + { wpa_printf(MSG_ERROR, "Could not generate random " "individual WEP key."); os_free(ikey); @@ -345,9 +344,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ - if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, - sta->addr, 0, 1, NULL, 0, ikey, - hapd->conf->individual_wep_key_len)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + sta->addr, 0, 1, NULL, 0, ikey, + hapd->conf->individual_wep_key_len)) { wpa_printf(MSG_ERROR, "Could not set individual WEP " "encryption."); } @@ -549,7 +548,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) + goto fail; + return; fail: @@ -652,7 +653,7 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, - sta->wps_ie, sta); + sta->wps_ie, sta->p2p_ie, sta); } @@ -673,6 +674,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_eapol_key *key; u16 datalen; struct rsn_pmksa_cache_entry *pmksa; + int key_mgmt; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->wps_state) @@ -681,9 +683,10 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " - "associated STA"); + "associated/Pre-authenticating STA"); return; } @@ -724,10 +727,19 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } - if ((!hapd->conf->ieee802_1x && - !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + if (!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "802.1X not enabled and WPS not used"); return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "STA is using PSK"); + return; + } if (!sta->eapol_sm) { sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); @@ -735,14 +747,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; #ifdef CONFIG_WPS - if (!hapd->conf->ieee802_1x && - ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == - WLAN_STA_MAYBE_WPS)) { - /* - * Delay EAPOL frame transmission until a possible WPS - * STA initiates the handshake with EAPOL-Start. - */ - sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x) { + u32 wflags = sta->flags & (WLAN_STA_WPS | + WLAN_STA_WPS2 | + WLAN_STA_MAYBE_WPS); + if (wflags == WLAN_STA_MAYBE_WPS || + wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a + * possible WPS STA initiates the handshake + * with EAPOL-Start. Only allow the wait to be + * skipped if the STA is known to support WPS + * 2.0. + */ + wpa_printf(MSG_DEBUG, "WPS: Do not start " + "EAPOL until EAPOL-Start is " + "received"); + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } } #endif /* CONFIG_WPS */ @@ -776,6 +798,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; @@ -788,11 +811,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); break; case IEEE802_1X_TYPE_EAPOL_KEY: wpa_printf(MSG_DEBUG, " EAPOL-Key"); - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!ap_sta_is_authorized(sta)) { wpa_printf(MSG_DEBUG, " Dropped key data from " "unauthorized Supplicant"); break; @@ -827,6 +851,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) struct rsn_pmksa_cache_entry *pmksa; int reassoc = 1; int force_1x = 0; + int key_mgmt; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->conf->wpa && @@ -840,9 +865,27 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if ((!force_1x && !hapd->conf->ieee802_1x) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + if (!force_1x && !hapd->conf->ieee802_1x) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " + "802.1X not enabled or forced for WPS"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPS to PSK. + */ + ieee802_1x_free_station(sta); return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ + ieee802_1x_free_station(sta); + return; + } if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -860,17 +903,40 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; - if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { /* - * Delay EAPOL frame transmission until a possible WPS - * initiates the handshake with EAPOL-Start. + * Delay EAPOL frame transmission until a possible WPS STA + * initiates the handshake with EAPOL-Start. Only allow the + * wait to be skipped if the STA is known to support WPS 2.0. */ + wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until " + "EAPOL-Start is received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FT - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FT information from R0KH. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + /* TODO: get vlan_id from R0KH using RRB message */ + return; + } +#endif /* CONFIG_IEEE80211R */ + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { int old_vlanid; @@ -885,6 +951,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); old_vlanid = sta->vlan_id; @@ -1380,8 +1447,8 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) os_free(eapol->default_wep_key); eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); if (eapol->default_wep_key == NULL || - os_get_random(eapol->default_wep_key, - hapd->conf->default_wep_key_len)) { + random_get_bytes(eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { printf("Could not generate random WEP key.\n"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; @@ -1432,10 +1499,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ - if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL, - eapol->default_wep_key_idx, 1, NULL, 0, - eapol->default_wep_key, - hapd->conf->default_wep_key_len)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + broadcast_ether_addr, + eapol->default_wep_key_idx, 1, NULL, 0, + eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); @@ -1538,6 +1606,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, os_memcpy(user->password, eap_user->password, eap_user->password_len); user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; user->ttls_auth = eap_user->ttls_auth; @@ -1651,6 +1720,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; conf.tnc = hapd->conf->tnc; conf.wps = hapd->wps; + conf.fragment_size = hapd->conf->fragment_size; + conf.pwd_group = hapd->conf->pwd_group; + conf.pbc_in_m1 = hapd->conf->pbc_in_m1; os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1669,7 +1741,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) return -1; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) return -1; #ifndef CONFIG_NO_RADIUS @@ -1680,9 +1752,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) if (hapd->conf->default_wep_key_len) { for (i = 0; i < 4; i++) - hapd->drv.set_key(hapd->conf->iface, hapd, - WPA_ALG_NONE, NULL, i, 0, NULL, 0, - NULL, 0); + hostapd_drv_set_key(hapd->conf->iface, hapd, + WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); ieee802_1x_rekey(hapd, NULL); @@ -1700,7 +1772,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; @@ -1739,9 +1811,19 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); + if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && + pos + sizeof(struct wpa_eapol_key) <= buf + len) { + const struct wpa_eapol_key *wpa; + wpa = (const struct wpa_eapol_key *) pos; + if (wpa->type == EAPOL_KEY_TYPE_RSN || + wpa->type == EAPOL_KEY_TYPE_WPA) + wpa_auth_eapol_key_tx_status(hapd->wpa_auth, + sta->wpa_sm, ack); + } + /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant * or Authenticator state machines, but EAPOL-Key packets are not - * retransmitted in case of failure. Try to re-sent failed EAPOL-Key + * retransmitted in case of failure. Try to re-send failed EAPOL-Key * packets couple of times because otherwise STA keys become * unsynchronized with AP. */ if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && @@ -1791,6 +1873,7 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { + *len = 0; if (sm == NULL) return NULL; @@ -1848,6 +1931,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; + struct os_time t; if (sm == NULL) return 0; @@ -1962,6 +2046,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, len += ret; /* dot1xAuthSessionStatsTable */ + os_get_time(&t); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -1976,8 +2061,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (time(NULL) - - sta->acct_session_start), + (unsigned int) (t.sec - sta->acct_session_start), sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; @@ -2004,14 +2088,18 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, "Added PMKSA cache entry (IEEE 802.1X)"); } -#ifdef CONFIG_WPS - if (!success && (sta->flags & WLAN_STA_WPS)) { + if (!success) { /* * Many devices require deauthentication after WPS provisioning * and some may not be be able to do that themselves, so - * disconnect the client here. + * disconnect the client here. In addition, this may also + * benefit IEEE 802.1X/EAPOL authentication cases, too since + * the EAPOL PAE state machine would remain in HELD state for + * considerable amount of time and some EAP methods, like + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. */ - wpa_printf(MSG_DEBUG, "WPS: Force disconnection after " + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Force disconnection after " "EAP-Failure"); /* Add a small sleep to increase likelihood of previously * requested EAP-Failure TX getting out before this should the @@ -2019,7 +2107,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, */ os_sleep(0, 10000); ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); + WLAN_REASON_IEEE_802_1X_AUTH_FAILED); } -#endif /* CONFIG_WPS */ } diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c new file mode 100644 index 0000000..6f8b778 --- /dev/null +++ b/src/ap/p2p_hostapd.c @@ -0,0 +1,120 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "p2p_hostapd.h" + + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + if (sta->p2p_ie == NULL) + return 0; + + return p2p_ie_text(sta->p2p_ie, buf, buf + buflen); +} + + +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d " + "duration=%d", count, start, duration); + + if (count == 0) { + hapd->noa_enabled = 0; + hapd->noa_start = 0; + hapd->noa_duration = 0; + } + + if (count != 255) { + wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set " + "NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + hapd->noa_enabled = 1; + hapd->noa_start = start; + hapd->noa_duration = duration; + + if (hapd->num_sta_no_p2p == 0) { + wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update " + "periodic NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable " + "periodic NoA"); + + return 0; +} + + +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA"); + hostapd_driver_set_noa(hapd, 0, 0, 0); + } +} + + +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA"); + hostapd_driver_set_noa(hapd, 255, hapd->noa_start, + hapd->noa_duration); + } +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_P2P_MANAGER +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid) +{ + u8 bitmap; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 4 + 3 + 1; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = P2P_OUI_TYPE; + + *eid++ = P2P_ATTR_MANAGEABILITY; + WPA_PUT_LE16(eid, 1); + eid += 2; + bitmap = P2P_MAN_DEVICE_MANAGEMENT; + if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION) + bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED; + bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL; + *eid++ = bitmap; + + return eid; +} +#endif /* CONFIG_P2P_MANAGER */ diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h new file mode 100644 index 0000000..95b31d9 --- /dev/null +++ b/src/ap/p2p_hostapd.h @@ -0,0 +1,41 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, 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. + */ + +#ifndef P2P_HOSTAPD_H +#define P2P_HOSTAPD_H + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd); +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd); + + +#else /* CONFIG_P2P */ + +static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_P2P */ + +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid); + +#endif /* P2P_HOSTAPD_H */ diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c index f68c479..b8fa5a9 100644 --- a/src/ap/peerkey_auth.c +++ b/src/ap/peerkey_auth.c @@ -18,6 +18,7 @@ #include "utils/eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" @@ -294,7 +295,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, return; } - if (os_get_random(smk, PMK_LEN)) { + if (random_get_bytes(smk, PMK_LEN)) { wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); return; } diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 335c9a5..82a3f69 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -17,9 +17,11 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "drivers/driver.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -30,14 +32,19 @@ #include "beacon.h" #include "ap_mlme.h" #include "vlan_init.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -121,11 +128,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) accounting_sta_stop(hapd, sta); + /* just in case */ + ap_sta_set_authorized(hapd, sta, 0); + if (sta->flags & WLAN_STA_WDS) - hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0); + hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0); if (!(sta->flags & WLAN_STA_PREAUTH)) - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -173,6 +183,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_P2P + if (sta->no_p2p_set) { + sta->no_p2p_set = 0; + hapd->num_sta_no_p2p--; + if (hapd->num_sta_no_p2p == 0) + hostapd_p2p_non_p2p_sta_disconnected(hapd); + } +#endif /* CONFIG_P2P */ + #if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N) if (hostapd_ht_operation_update(hapd->iface) > 0) set_beacon++; @@ -183,6 +202,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ieee802_1x_free_station(sta); wpa_auth_sta_deinit(sta->wpa_sm); @@ -199,7 +220,12 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); +#endif /* CONFIG_P2P */ + wpabuf_free(sta->wps_ie); + wpabuf_free(sta->p2p_ie); os_free(sta->ht_capabilities); @@ -253,27 +279,41 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; - wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", - MAC2STR(sta->addr)); - inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr); + inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr); if (inactive_sec == -1) { - wpa_printf(MSG_DEBUG, "Could not get station info " - "from kernel driver for " MACSTR ".", - MAC2STR(sta->addr)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Check inactivity: Could not " + "get station info from kernel driver for " + MACSTR, MAC2STR(sta->addr)); + /* + * The driver may not support this functionality. + * Anyway, try again after the next inactivity timeout, + * but do not disconnect the station now. + */ + next_time = hapd->conf->ap_max_inactivity; } else if (inactive_sec < hapd->conf->ap_max_inactivity && sta->flags & WLAN_STA_ASSOC) { /* station activity detected; reset timeout state */ - wpa_printf(MSG_DEBUG, " Station has been active"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been active %is ago", + MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; next_time = hapd->conf->ap_max_inactivity - inactive_sec; + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been " + "inactive too long: %d sec, max allowed: %d", + MAC2STR(sta->addr), inactive_sec, + hapd->conf->ap_max_inactivity); } } if ((sta->flags & WLAN_STA_ASSOC) && sta->timeout_next == STA_DISASSOC && !(sta->flags & WLAN_STA_PENDING_POLL)) { - wpa_printf(MSG_DEBUG, " Station has ACKed data poll"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR + " has ACKed data poll", MAC2STR(sta->addr)); /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; @@ -288,40 +328,10 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if (sta->timeout_next == STA_NULLFUNC && (sta->flags & WLAN_STA_ASSOC)) { -#ifndef CONFIG_NATIVE_WINDOWS - /* send data frame to poll STA and check whether this frame - * is ACKed */ - struct ieee80211_hdr hdr; - - wpa_printf(MSG_DEBUG, " Polling STA with data frame"); + wpa_printf(MSG_DEBUG, " Polling STA"); sta->flags |= WLAN_STA_PENDING_POLL; - - os_memset(&hdr, 0, sizeof(hdr)); - if (hapd->driver && - os_strcmp(hapd->driver->name, "hostap") == 0) { - /* - * WLAN_FC_STYPE_NULLFUNC would be more appropriate, - * but it is apparently not retried so TX Exc events - * are not received for it. - */ - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_DATA); - } else { - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_NULLFUNC); - } - - hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); - os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, - ETH_ALEN); - os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); - - if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0) - perror("ap_handle_timer: send"); -#endif /* CONFIG_NATIVE_WINDOWS */ + hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr, + sta->flags & WLAN_STA_WMM); } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; @@ -330,10 +340,11 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) MAC2STR(sta->addr)); if (deauth) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth( + hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } else { - hapd->drv.sta_disassoc( + hostapd_drv_sta_disassoc( hapd, sta->addr, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } @@ -346,6 +357,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) hapd, sta); break; case STA_DISASSOC: + ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); if (!sta->acct_terminate_cause) @@ -397,7 +409,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); - hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -463,7 +475,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); - if (hapd->drv.sta_remove(hapd, sta->addr) && + if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR " from kernel driver.", MAC2STR(sta->addr)); @@ -498,13 +510,23 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, } +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); +} + + void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~WLAN_STA_ASSOC; - ap_sta_remove(hapd, sta); + ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, @@ -512,7 +534,22 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - mlme_disassociate_indication(hapd, sta, reason); + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_disassoc_cb_timeout, hapd, sta); +} + + +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -522,7 +559,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - ap_sta_remove(hapd, sta); + ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, @@ -530,7 +567,12 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); - mlme_deauthenticate_indication(hapd, sta, reason); + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, sta); } @@ -636,7 +678,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); - ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); + ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); if (ret < 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " @@ -732,6 +774,64 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_IEEE80211W */ +void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, + int authorized) +{ + const u8 *dev_addr = NULL; + if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) + return; + +#ifdef CONFIG_P2P + dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); +#endif /* CONFIG_P2P */ + + if (authorized) { + if (dev_addr) + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED + MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED + MACSTR, MAC2STR(sta->addr)); + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED MACSTR " p2p_dev_addr=" + MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + + sta->flags |= WLAN_STA_AUTHORIZED; + } else { + if (dev_addr) + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED + MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED + MACSTR, MAC2STR(sta->addr)); + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED MACSTR " p2p_dev_addr=" + MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); + else if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_AUTHORIZED; + } + + if (hapd->sta_authorized_cb) + hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, + sta->addr, authorized, dev_addr); +} + + void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason) { @@ -740,12 +840,47 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, sta = ap_get_sta(hapd, addr); if (addr) - hapd->drv.sta_deauth(hapd, addr, reason); + hostapd_drv_sta_deauth(hapd, addr, reason); if (sta == NULL) return; - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); + ap_sta_set_authorized(hapd, sta, 0); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; + + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, sta); +} + + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) { + wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + ap_sta_deauth_cb_timeout(hapd, sta); +} + + +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) { + wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_disassoc_cb_timeout(hapd, sta); } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 55faa5a..3ad00e2 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -31,6 +31,10 @@ #define WLAN_STA_WPS BIT(12) #define WLAN_STA_MAYBE_WPS BIT(13) #define WLAN_STA_WDS BIT(14) +#define WLAN_STA_ASSOC_REQ_OK BIT(15) +#define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_PENDING_DISASSOC_CB BIT(29) +#define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) /* Maximum number of supported rates (from both Supported Rates and Extended @@ -55,6 +59,7 @@ struct sta_info { unsigned int no_ht_gf_set:1; unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; + unsigned int no_p2p_set:1; u16 auth_alg; u8 previous_ap[6]; @@ -63,6 +68,9 @@ struct sta_info { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE } timeout_next; + u16 deauth_reason; + u16 disassoc_reason; + /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; @@ -104,6 +112,7 @@ struct sta_info { #endif /* CONFIG_IEEE80211W */ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ + struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ }; @@ -111,7 +120,7 @@ struct sta_info { * passed since last received frame from the station, a nullfunc data frame is * sent to the station. If this frame is not acknowledged and no other frames * have been received, the station will be disassociated after - * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) #define AP_DISASSOC_DELAY (1) @@ -152,4 +161,14 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); +void ap_sta_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +static inline int ap_sta_is_authorized(struct sta_info *sta) +{ + return sta->flags & WLAN_STA_AUTHORIZED; +} + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); + #endif /* STA_INFO_H */ diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 9690348..fac7f4b 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -1,6 +1,6 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -21,6 +21,7 @@ #include "sta_info.h" #include "ap_mlme.h" #include "wpa_auth.h" +#include "ap_drv_ops.h" #include "tkip_countermeasures.h" @@ -29,7 +30,7 @@ static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, { struct hostapd_data *hapd = eloop_ctx; hapd->tkip_countermeasures = 0; - hapd->drv.set_countermeasures(hapd, 0); + hostapd_drv_set_countermeasures(hapd, 0); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); } @@ -44,24 +45,30 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) wpa_auth_countermeasures_start(hapd->wpa_auth); hapd->tkip_countermeasures = 1; - hapd->drv.set_countermeasures(hapd, 1); + hostapd_drv_set_countermeasures(hapd, 1); wpa_gtk_rekey(hapd->wpa_auth); eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - hapd->drv.sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_AUTHORIZED); - hapd->drv.sta_remove(hapd, sta->addr); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + hostapd_drv_sta_remove(hapd, sta->addr); } } +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); +} + + void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { - time_t now; + struct os_time now; if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -81,13 +88,13 @@ void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) } } - time(&now); - if (now > hapd->michael_mic_failure + 60) { + os_get_time(&now); + if (now.sec > hapd->michael_mic_failure + 60) { hapd->michael_mic_failures = 1; } else { hapd->michael_mic_failures++; if (hapd->michael_mic_failures > 1) ieee80211_tkip_countermeasures_start(hapd); } - hapd->michael_mic_failure = now; + hapd->michael_mic_failure = now.sec; } diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h index 5a1afce..a8ffd16 100644 --- a/src/ap/tkip_countermeasures.h +++ b/src/ap/tkip_countermeasures.h @@ -1,6 +1,6 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -16,5 +16,6 @@ #define TKIP_COUNTERMEASURES_H void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd); #endif /* TKIP_COUNTERMEASURES_H */ diff --git a/src/ap/utils.c b/src/ap/utils.c index 0ff48ae..09bc32f 100644 --- a/src/ap/utils.c +++ b/src/ap/utils.c @@ -22,6 +22,7 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, + const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len), void *ctx) { diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index c9d166a..f2f766f 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -19,6 +19,7 @@ #include "utils/common.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "vlan_init.h" @@ -737,9 +738,10 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { if (mssid->wep.key[i] && - hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == mssid->wep.idx, NULL, 0, - mssid->wep.key[i], mssid->wep.len[i])) { + hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, + i == mssid->wep.idx, NULL, 0, + mssid->wep.key[i], mssid->wep.len[i])) + { wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " "encryption for dynamic VLAN"); return -1; @@ -755,7 +757,7 @@ static int vlan_dynamic_add(struct hostapd_data *hapd, { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { - if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) { + if (hostapd_vlan_if_add(hapd, vlan->ifname)) { if (errno != EEXIST) { wpa_printf(MSG_ERROR, "VLAN: Could " "not add VLAN %s: %s", @@ -785,7 +787,7 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, next = vlan->next; if (vlan->vlan_id != VLAN_ID_WILDCARD && - hapd->drv.vlan_if_remove(hapd, vlan->ifname)) { + hostapd_vlan_if_remove(hapd, vlan->ifname)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); @@ -859,7 +861,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, pos); os_free(ifname); - if (hapd->drv.vlan_if_add(hapd, n->ifname)) { + if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); return NULL; } @@ -897,7 +899,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) return 1; if (vlan->dynamic_vlan == 0) - hapd->drv.vlan_if_remove(hapd, vlan->ifname); + hostapd_vlan_if_remove(hapd, vlan->ifname); return 0; } diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 3668130..82177f3 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -23,6 +23,7 @@ #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "wmm.h" @@ -74,6 +75,8 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) if (hapd->conf->wmm_uapsd) wmm->qos_info |= 0x80; + wmm->reserved = 0; + /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; @@ -94,9 +97,11 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) } -/* This function is called when a station sends an association request with - * WMM info element. The function returns zero on success or non-zero on any - * error in WMM element. eid does not include Element ID and Length octets. */ +/* + * This function is called when a station sends an association request with + * WMM info element. The function returns 1 on success or 0 on any error in WMM + * element. eid does not include Element ID and Length octets. + */ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) { struct wmm_information_element *wmm; @@ -106,7 +111,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) if (len < sizeof(struct wmm_information_element)) { wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); - return -1; + return 0; } wmm = (struct wmm_information_element *) eid; @@ -117,10 +122,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || wmm->version != WMM_VERSION) { wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); - return -1; + return 0; } - return 0; + return 1; } @@ -150,7 +155,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; - if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0) + if (hostapd_drv_send_mlme(hapd, m, len) < 0) perror("wmm_send_action: send"); } diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 36cb0f4..34202b6 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -22,6 +22,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" #include "ieee802_11.h" @@ -44,6 +45,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -215,11 +218,13 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; - if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); } else { wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + wpa_hexdump_key(MSG_DEBUG, "GMK", + wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { @@ -296,13 +301,41 @@ static void wpa_group_set_key_len(struct wpa_group *group, int cipher) } -static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_group *group; u8 buf[ETH_ALEN + 8 + sizeof(group)]; u8 rkey[32]; + if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (random_get_bytes(rkey, sizeof(rkey)) < 0) + return -1; + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; +} + + +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id, int delay_init) +{ + struct wpa_group *group; + group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) return NULL; @@ -312,27 +345,35 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "for secure operations - update keys later when " + "the first station connects"); + } + + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); - if (os_get_random(rkey, sizeof(rkey)) || - os_get_random(group->GMK, WPA_GMK_LEN)) { + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - group->Counter, WPA_NONCE_LEN); - group->GInit = TRUE; - wpa_group_sm_step(wpa_auth, group); - group->GInit = FALSE; - wpa_group_sm_step(wpa_auth, group); + if (delay_init) { + wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " + "until Beacon frames have been configured"); + /* Initialization is completed in wpa_init_keys(). */ + } else { + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + } return group; } @@ -364,7 +405,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } - wpa_auth->group = wpa_group_init(wpa_auth, 0); + wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); @@ -405,6 +446,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr, } +int wpa_init_keys(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " + "keys"); + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + return 0; +} + + /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() @@ -539,6 +593,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) static void wpa_free_sta_sm(struct wpa_state_machine *sm) { + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); #endif /* CONFIG_IEEE80211R */ @@ -563,6 +621,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { @@ -651,6 +710,37 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_IEEE80211R */ +static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int group) +{ + /* Supplicant reported a Michael MIC error */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure (group=%d))", + group); + + if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "group cipher is not TKIP"); + } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "pairwise cipher is not TKIP"); + } else { + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + } + + /* + * Error report is not a request for a new key handshake, but since + * Authenticator may do it, let's change the keys now anyway. + */ + wpa_request_new_ptk(sm); +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -676,6 +766,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, key = (struct wpa_eapol_key *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); key_data_length = WPA_GET_BE16(key->key_data_length); + wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR + " key_info=0x%x type=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, key_data_length); if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -686,7 +779,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (sm->wpa == WPA_VERSION_WPA2) { - if (key->type != EAPOL_KEY_TYPE_RSN) { + if (key->type == EAPOL_KEY_TYPE_WPA) { + /* + * Some deployed station implementations seem to send + * msg 4/4 with incorrect type value in WPA2 mode. + */ + wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " + "with unexpected WPA type in RSN mode"); + } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); @@ -701,6 +801,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } + wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ @@ -770,7 +875,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, if (!(key_info & WPA_KEY_INFO_REQUEST) && !wpa_replay_counter_valid(sm, key->replay_counter)) { int i; - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "received EAPOL-Key %s with unexpected " "replay counter", msgtxt); for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { @@ -795,6 +900,26 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, sm->wpa_ptk_state); return; } + random_add_randomness(key->key_nonce, WPA_NONCE_LEN); + if (sm->group->reject_4way_hs_for_entropy) { + /* + * The system did not have enough entropy to generate + * strong random numbers. Reject the first 4-way + * handshake(s) and collect some entropy based on the + * information from it. Once enough entropy is + * available, the next atempt will trigger GMK/Key + * Counter update and the station will be allowed to + * continue. + */ + wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " + "collect more entropy for random number " + "generation"); + sm->group->reject_4way_hs_for_entropy = FALSE; + random_mark_pool_ready(); + sm->group->first_sta_seen = FALSE; + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, @@ -905,6 +1030,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -930,17 +1056,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { - /* Supplicant reported a Michael MIC error */ - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - wpa_auth_mic_failure_report(wpa_auth, sm->addr); - sm->dot11RSNAStatsTKIPRemoteMICFailures++; - wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; - /* Error report is not a request for a new key - * handshake, but since Authenticator may do it, let's - * change the keys now anyway. */ - wpa_request_new_ptk(sm); + wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)); } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " @@ -958,9 +1076,6 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for GTK " "rekeying"); - /* FIX: why was this triggering PTK rekeying for the - * STA that requested Group Key rekeying?? */ - /* wpa_request_new_ptk(sta->wpa_sm); */ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); wpa_rekey_gtk(wpa_auth, NULL); } @@ -987,6 +1102,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; + sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); @@ -995,25 +1111,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } -static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, - u8 *gtk, size_t gtk_len) +static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, + const u8 *gnonce, u8 *gtk, size_t gtk_len) { - u8 data[ETH_ALEN + WPA_NONCE_LEN]; + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8 *pos; + int ret = 0; - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + /* GTK = PRF-X(GMK, "Group key expansion", + * AA || GNonce || Time || random data) + * The example described in the IEEE 802.11 standard uses only AA and + * GNonce as inputs here. Add some more entropy since this derivation + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + pos = data + ETH_ALEN + WPA_NONCE_LEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + if (random_get_bytes(pos, 16) < 0) + ret = -1; #ifdef CONFIG_IEEE80211W - sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) + ret = -1; #endif /* CONFIG_IEEE80211W */ - wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); + return ret; } @@ -1022,6 +1150,7 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; + sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); @@ -1209,10 +1338,14 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; - if (ctr == 1) + if (ctr == 1 && wpa_auth->conf.tx_status) timeout_ms = eapol_key_timeout_first; else timeout_ms = eapol_key_timeout_subseq; + if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) + sm->pending_1_of_4_timeout = 1; + wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " + "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1247,8 +1380,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "", - 0); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1411,10 +1543,41 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static void wpa_group_first_station(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "to proceed - reject first 4-way handshake"); + group->reject_4way_hs_for_entropy = TRUE; + } + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + + if (!sm->group->first_sta_seen) { + wpa_group_first_station(sm->wpa_auth, sm->group); + sm->group->first_sta_seen = TRUE; + } + os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, + WPA_NONCE_LEN); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more @@ -1605,6 +1768,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #endif /* CONFIG_IEEE80211R */ + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { @@ -1726,6 +1890,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk_len = 0; keyidx = 0; _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } } kde_len = wpa_ie_len + ieee80211w_kde_len(sm); @@ -1876,8 +2054,11 @@ SM_STEP(WPA_PTK) if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) @@ -1913,6 +2094,8 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; @@ -1933,6 +2116,9 @@ SM_STEP(WPA_PTK) else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %d reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -1956,6 +2142,10 @@ SM_STEP(WPA_PTK) else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %d " + "reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -2094,20 +2284,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, { int ret = 0; - /* FIX: is this the correct way of getting GNonce? */ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, - group->GTK[group->GN - 1], group->GTK_len); + if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", + wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + wpa_hexdump_key(MSG_DEBUG, "GTK", + group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - if (os_get_random(group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to get new random " - "IGTK"); + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", + wpa_auth->addr, group->GNonce, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) ret = -1; - } wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); } @@ -2143,20 +2337,23 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); + sm->GUpdateStationKeys = FALSE; return 0; } if (sm->GUpdateStationKeys) { /* - * This should not really happen, but just in case, make sure - * we do not count the same STA twice in GKeyDoneStations. + * This should not really happen, so add a debug log entry. + * Since we clear the GKeyDoneStations before the loop, the + * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "GUpdateStationKeys already set - do not " - "increment GKeyDoneStations"); - } else { - sm->group->GKeyDoneStations++; - sm->GUpdateStationKeys = TRUE; + "GUpdateStationKeys was already set when " + "marking station for GTK rekeying"); } + + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); return 0; } @@ -2185,32 +2382,54 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); + if (group->GKeyDoneStations) { + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " + "GKeyDoneStations=%d when starting new GTK rekey", + group->GKeyDoneStations); + group->GKeyDoneStations = 0; + } wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } -static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + if (wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_alg_enum(wpa_auth->conf.wpa_group), + broadcast_ether_addr, group->GN, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_enum(wpa_auth->conf.wpa_group), - NULL, group->GN, group->GTK[group->GN - 1], - group->GTK_len); -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { - wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, - NULL, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN); - } -#endif /* CONFIG_IEEE80211W */ + if (wpa_group_config_group_keys(wpa_auth, group) < 0) + return -1; + + return 0; } @@ -2310,6 +2529,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); } } @@ -2345,19 +2565,21 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { int len = 0, ret; char pmkid_txt[PMKID_LEN * 2 + 1]; +#ifdef CONFIG_RSN_PREAUTH + const int preauth = 1; +#else /* CONFIG_RSN_PREAUTH */ + const int preauth = 0; +#endif /* CONFIG_RSN_PREAUTH */ if (wpa_auth == NULL) return len; ret = os_snprintf(buf + len, buflen - len, "dot11RSNAOptionImplemented=TRUE\n" -#ifdef CONFIG_RSN_PREAUTH - "dot11RSNAPreauthenticationImplemented=TRUE\n" -#else /* CONFIG_RSN_PREAUTH */ - "dot11RSNAPreauthenticationImplemented=FALSE\n" -#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); if (ret < 0 || (size_t) ret >= buflen - len) @@ -2473,7 +2695,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, @@ -2570,7 +2792,8 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int session_timeout, struct eapol_state_machine *eapol) { - if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, @@ -2609,7 +2832,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); - group = wpa_group_init(wpa_auth, vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; @@ -2649,3 +2872,33 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) sm->group = group; return 0; } + + +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack) +{ + if (wpa_auth == NULL || sm == NULL) + return; + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR + " ack=%d", MAC2STR(sm->addr), ack); + if (sm->pending_1_of_4_timeout && ack) { + /* + * Some deployed supplicant implementations update their SNonce + * for each EAPOL-Key 2/4 message even within the same 4-way + * handshake and then fail to use the first SNonce when + * deriving the PTK. This results in unsuccessful 4-way + * handshake whenever the relatively short initial timeout is + * reached and EAPOL-Key 1/4 is retransmitted. Try to work + * around this by increasing the timeout now that we know that + * the station has received the frame. + */ + int timeout_ms = eapol_key_timeout_subseq; + wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " + "timeout by %u ms because of acknowledged frame", + timeout_ms); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_register_timeout(timeout_ms / 1000, + (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } +} diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index d0136c7..ce2751e 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -143,7 +143,9 @@ struct wpa_auth_config { int peerkey; int wmm_enabled; int wmm_uapsd; + int disable_pmksa_caching; int okc; + int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ @@ -160,6 +162,7 @@ struct wpa_auth_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #endif /* CONFIG_IEEE80211R */ }; @@ -205,6 +208,7 @@ struct wpa_auth_callbacks { struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); +int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); @@ -259,6 +263,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index c9871d9..4a7d619 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -18,6 +18,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" #include "wmm.h" @@ -28,28 +29,6 @@ #ifdef CONFIG_IEEE80211R -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *ric; - size_t ric_len; -}; - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse); - - static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { @@ -91,7 +70,9 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; - capab = RSN_FT_CAPAB_FT_OVER_DS; + capab = 0; + if (conf->ft_over_ds) + capab |= RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf; @@ -334,7 +315,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ - if (os_get_random(f.nonce, sizeof(f.nonce))) { + if (random_get_bytes(f.nonce, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; @@ -725,143 +706,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, } -static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - - parse->ftie = ie; - parse->ftie_len = ie_len; - - pos = ie + sizeof(struct rsn_ftie); - end = ie + ie_len; - - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r1kh_id = pos + 2; - break; - case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; - break; - case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; - break; - } - - pos += 2 + pos[1]; - } - - return 0; -} - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - struct wpa_ie_data data; - int ret; - const struct rsn_ftie *ftie; - int prot_ie_count = 0; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) - return -1; - ftie = (const struct rsn_ftie *) (pos + 2); - prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - case WLAN_EID_RIC_DATA: - if (parse->ric == NULL) - parse->ric = pos; - } - - pos += 2 + pos[1]; - } - - if (prot_ie_count == 0) - return 0; /* no MIC */ - - /* - * Check that the protected IE count matches with IEs included in the - * frame. - */ - if (parse->rsn) - prot_ie_count--; - if (parse->mdie) - prot_ie_count--; - if (parse->ftie) - prot_ie_count--; - if (prot_ie_count < 0) { - wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " - "the protected IE count"); - return -1; - } - - if (prot_ie_count == 0 && parse->ric) { - wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " - "included in protected IE count"); - return -1; - } - - /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; - } - parse->ric_len = pos - parse->ric; - if (prot_ie_count) { - wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " - "frame", (int) prot_ie_count); - return -1; - } - - return 0; -} - - static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, @@ -997,7 +841,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); - if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1204,7 +1048,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, count = 3; if (parse.ric) - count++; + count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index afa13a6..d44381d 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,6 +1,6 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2011, Jouni Malinen * * 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 @@ -31,15 +31,10 @@ #include "wpa_auth.h" -#ifdef CONFIG_IEEE80211R -static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len); -#endif /* CONFIG_IEEE80211R */ - - static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, struct wpa_auth_config *wconf) { + os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->wpa_pairwise = conf->wpa_pairwise; @@ -54,6 +49,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; + wconf->disable_pmksa_caching = conf->disable_pmksa_caching; wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; @@ -77,6 +73,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->r0kh_list = conf->r0kh_list; wconf->r1kh_list = conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; + wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ } @@ -230,8 +227,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, return -1; } - return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, - key, key_len); + return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, + key, key_len); } @@ -248,7 +245,15 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, int encrypt) { struct hostapd_data *hapd = ctx; - return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt); + struct sta_info *sta; + u32 flags = 0; + + sta = ap_get_sta(hapd, addr); + if (sta) + flags = hostapd_sta_flags_to_drv(sta->flags); + + return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len, + encrypt, flags); } @@ -327,8 +332,9 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) MAC2STR(idata->src_hapd->own_addr), idata->src_hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->iface); - hostapd_rrb_receive(hapd, idata->src_hapd->own_addr, - idata->data, idata->data_len); + wpa_ft_rrb_rx(hapd->wpa_auth, + idata->src_hapd->own_addr, + idata->data, idata->data_len); return 1; } } @@ -343,6 +349,8 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len) { struct hostapd_data *hapd = ctx; + struct l2_ethhdr *buf; + int ret; #ifdef CONFIG_IEEE80211R if (proto == ETH_P_RRB && hapd->iface->for_each_interface) { @@ -366,7 +374,18 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, data, data_len); if (hapd->l2 == NULL) return -1; - return l2_packet_send(hapd->l2, dst, proto, data, data_len); + + buf = os_malloc(sizeof(*buf) + data_len); + if (buf == NULL) + return -1; + os_memcpy(buf->h_dest, dst, ETH_ALEN); + os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN); + buf->h_proto = host_to_be16(proto); + os_memcpy(buf + 1, data, data_len); + ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf, + sizeof(*buf) + data_len); + os_free(buf); + return ret; } @@ -396,7 +415,7 @@ static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); os_memcpy(&m->u, data, data_len); - res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen); + res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen); os_free(m); return res; } @@ -431,7 +450,14 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; - wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); + struct l2_ethhdr *ethhdr; + if (len < sizeof(*ethhdr)) + return; + ethhdr = (struct l2_ethhdr *) buf; + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), + len - sizeof(*ethhdr)); } #endif /* CONFIG_IEEE80211R */ @@ -445,6 +471,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) size_t wpa_ie_len; hostapd_wpa_auth_conf(hapd->conf, &_conf); + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) + _conf.tx_status = 1; os_memset(&cb, 0, sizeof(cb)); cb.ctx = hapd; cb.logger = hostapd_wpa_auth_logger; @@ -494,7 +522,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : hapd->conf->iface, NULL, ETH_P_RRB, - hostapd_rrb_receive, hapd, 0); + hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || hapd->driver->send_ether == NULL)) { @@ -520,6 +548,7 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd) void hostapd_deinit_wpa(struct hostapd_data *hapd) { + ieee80211_tkip_countermeasures_deinit(hapd); rsn_preauth_iface_deinit(hapd); if (hapd->wpa_auth) { wpa_deinit(hapd->wpa_auth); diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b69129f..d82192a 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -86,6 +86,7 @@ struct wpa_state_machine { unsigned int pending_deinit:1; unsigned int started:1; unsigned int mgmt_frame_prot:1; + unsigned int rx_eapol_key_secure:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; @@ -120,6 +121,8 @@ struct wpa_state_machine { * message 2/4 */ u8 *assoc_resp_ftie; #endif /* CONFIG_IEEE80211R */ + + int pending_1_of_4_timeout; }; @@ -145,6 +148,8 @@ struct wpa_group { u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; + Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f8a1804..5e8d134 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -25,6 +25,11 @@ #include "wpa_auth_i.h" +#ifdef CONFIG_RSN_TESTING +int rsn_testing = 0; +#endif /* CONFIG_RSN_TESTING */ + + static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; @@ -141,6 +146,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, count = pos; pos += 2; +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; @@ -157,6 +170,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->rsn_pairwise); @@ -168,6 +189,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, count = pos; pos += 2; +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += RSN_SELECTOR_LEN; @@ -203,6 +232,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", conf->wpa_key_mgmt); @@ -227,6 +264,10 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) + capab |= BIT(8) | BIT(14) | BIT(15); +#endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -256,6 +297,29 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + /* + * Fill in any defined fields and add extra data to the end of + * the element. + */ + int pmkid_count_set = pmkid != NULL; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + pmkid_count_set = 1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } + + os_memset(pos, 0x12, 17); + pos += 17; + } +#endif /* CONFIG_RSN_TESTING */ + hdr->len = (pos - buf) - 2; return pos - buf; @@ -322,114 +386,6 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, } -static int wpa_selector_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) - return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) - return WPA_CIPHER_TKIP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) - return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) - return WPA_KEY_MGMT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) - return WPA_KEY_MGMT_PSK; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) - return WPA_KEY_MGMT_WPA_NONE; - return 0; -} - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - const struct wpa_ie_hdr *hdr; - const u8 *pos; - int left; - int i, count; - - os_memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - data->mgmt_group_cipher = 0; - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) - return -1; - - hdr = (const struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || - hdr->len != wpa_ie_len - 2 || - RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || - WPA_GET_LE16(hdr->version) != WPA_VERSION) { - return -2; - } - - pos = (const u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - } - - if (left > 0) { - return -8; - } - - return 0; -} - - struct wpa_auth_okc_iter_data { struct rsn_pmksa_cache_entry *pmksa; const u8 *aa; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index a6ffd4d..4b31d4f 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -28,6 +28,7 @@ #include "wps/wps_dev_attr.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "beacon.h" #include "sta_info.h" #include "wps_hostapd.h" @@ -40,11 +41,51 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd, static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); #endif /* CONFIG_WPS_UPNP */ -static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); +struct wps_for_each_data { + int (*func)(struct hostapd_data *h, void *ctx); + void *ctx; +}; + + +static int wps_for_each(struct hostapd_iface *iface, void *ctx) +{ + struct wps_for_each_data *data = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + int ret = data->func(hapd, data->ctx); + if (ret) + return ret; + } + + return 0; +} + + +static int hostapd_wps_for_each(struct hostapd_data *hapd, + int (*func)(struct hostapd_data *h, void *ctx), + void *ctx) +{ + struct hostapd_iface *iface = hapd->iface; + struct wps_for_each_data data; + data.func = func; + data.ctx = ctx; + if (iface->for_each_interface == NULL) + return wps_for_each(iface, &data); + return iface->for_each_interface(iface->interfaces, wps_for_each, + &data); +} + + static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, size_t psk_len) { @@ -100,8 +141,9 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, hapd->wps_beacon_ie = beacon_ie; wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = probe_resp_ie; - ieee802_11_set_beacon(hapd); - return hapd->drv.set_ap_wps_ie(hapd); + if (hapd->beacon_set_done) + ieee802_11_set_beacon(hapd); + return hostapd_set_ap_wps_ie(hapd); } @@ -144,11 +186,26 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, } +struct wps_stop_reg_data { + struct hostapd_data *current_hapd; + const u8 *uuid_e; +}; + +static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) +{ + struct wps_stop_reg_data *data = ctx; + if (hapd != data->current_hapd && hapd->wps != NULL) + wps_registrar_complete(hapd->wps->registrar, data->uuid_e); + return 0; +} + + static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e) { struct hostapd_data *hapd = ctx; char uuid[40]; + struct wps_stop_reg_data data; if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) return; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", @@ -156,6 +213,9 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, if (hapd->wps_reg_success_cb) hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, mac_addr, uuid_e); + data.current_hapd = hapd; + data.uuid_e = uuid_e; + hostapd_wps_for_each(hapd, wps_stop_registrar, &data); } @@ -200,9 +260,23 @@ static void wps_reload_config(void *eloop_data, void *user_ctx) } -static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, + size_t attr_len) { - struct hostapd_data *hapd = ctx; + size_t blen = attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, attr, attr_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_NEW_AP_SETTINGS "%s", buf); + os_free(buf); + } +} + + +static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) +{ + const struct wps_credential *cred = ctx; FILE *oconf, *nconf; size_t len, i; char *tmp_fname; @@ -210,6 +284,9 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) int multi_bss; int wpa; + if (hapd->wps == NULL) + return 0; + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); @@ -226,15 +303,15 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) if ((hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { - size_t blen = cred->cred_attr_len * 2 + 1; - char *_buf = os_malloc(blen); - if (_buf) { - wpa_snprintf_hex(_buf, blen, - cred->cred_attr, cred->cred_attr_len); - wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s", - WPS_EVENT_NEW_AP_SETTINGS, _buf); - os_free(_buf); - } + hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); + } else if (hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) { + struct wpabuf *attr; + attr = wpabuf_alloc(200); + if (attr && wps_build_credential_wrap(attr, cred) == 0) + hapd_new_ap_event(hapd, wpabuf_head_u8(attr), + wpabuf_len(attr)); + wpabuf_free(attr); } else wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); @@ -384,6 +461,8 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) if (!multi_bss && (str_starts(buf, "ssid=") || str_starts(buf, "auth_algs=") || + str_starts(buf, "wep_default_key=") || + str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || @@ -414,20 +493,27 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, NULL); - /* TODO: dualband AP may need to update multiple configuration files */ - wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); return 0; } +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); +} + + static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = eloop_data; if (hapd->conf->ap_setup_locked) return; + if (hapd->ap_pin_failures_consecutive >= 10) + return; wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); @@ -436,11 +522,12 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) } -static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, - struct wps_event_pwd_auth_fail *data) +static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) { - if (!data->enrollee || hapd->conf->ap_pin == NULL) - return; + struct wps_event_pwd_auth_fail *data = ctx; + + if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; /* * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup @@ -448,17 +535,27 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, * force attacks. */ hapd->ap_pin_failures++; - wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", - hapd->ap_pin_failures); + hapd->ap_pin_failures_consecutive++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " + "(%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); if (hapd->ap_pin_failures < 3) - return; + return 0; wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); hapd->wps->ap_setup_locked = 1; wps_registrar_update_ie(hapd->wps->registrar); - if (!hapd->conf->ap_setup_locked) { + if (!hapd->conf->ap_setup_locked && + hapd->ap_pin_failures_consecutive >= 10) { + /* + * In indefinite lockdown - disable automatic AP PIN + * reenablement. + */ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); + } else if (!hapd->conf->ap_setup_locked) { if (hapd->ap_pin_lockout_time == 0) hapd->ap_pin_lockout_time = 60; else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && @@ -473,7 +570,60 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, NULL); } - /* TODO: dualband AP may need to update other interfaces */ + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); +} + + +static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + if (hapd->ap_pin_failures_consecutive == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " + "- total validation failures %u (%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + hapd->ap_pin_failures_consecutive = 0; + + return 0; +} + + +static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); +} + + +static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { + "No Error", /* WPS_EI_NO_ERROR */ + "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ + "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ +}; + +static void hostapd_wps_event_fail(struct hostapd_data *hapd, + struct wps_event_fail *fail) +{ + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + } } @@ -482,8 +632,43 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event, { struct hostapd_data *hapd = ctx; - if (event == WPS_EV_PWD_AUTH_FAIL) + switch (event) { + case WPS_EV_M2D: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); + break; + case WPS_EV_FAIL: + hostapd_wps_event_fail(hapd, &data->fail); + break; + case WPS_EV_SUCCESS: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); + break; + case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); + break; + case WPS_EV_PBC_OVERLAP: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); + break; + case WPS_EV_PBC_TIMEOUT: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); + break; + case WPS_EV_ER_AP_ADD: + break; + case WPS_EV_ER_AP_REMOVE: + break; + case WPS_EV_ER_ENROLLEE_ADD: + break; + case WPS_EV_ER_ENROLLEE_REMOVE: + break; + case WPS_EV_ER_AP_SETTINGS: + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + break; + case WPS_EV_AP_PIN_SUCCESS: + hostapd_wps_ap_pin_success(hapd); + break; + } + if (hapd->wps_event_cb) + hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); } @@ -495,7 +680,81 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; - hapd->drv.set_ap_wps_ie(hapd); + hostapd_set_ap_wps_ie(hapd); +} + + +static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) +{ + const u8 **uuid = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) { + *uuid = hapd->wps->uuid; + return 1; + } + } + + return 0; +} + + +static const u8 * get_own_uuid(struct hostapd_iface *iface) +{ + const u8 *uuid; + if (iface->for_each_interface == NULL) + return NULL; + uuid = NULL; + iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid); + return uuid; +} + + +static int count_interface_cb(struct hostapd_iface *iface, void *ctx) +{ + int *count= ctx; + (*count)++; + return 0; +} + + +static int interface_count(struct hostapd_iface *iface) +{ + int count = 0; + if (iface->for_each_interface == NULL) + return 0; + iface->for_each_interface(iface->interfaces, count_interface_cb, + &count); + return count; +} + + +static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, + struct wps_context *wps) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(wps->dev.vendor_ext[i]); + wps->dev.vendor_ext[i] = NULL; + + if (hapd->conf->wps_vendor_ext[i] == NULL) + continue; + + wps->dev.vendor_ext[i] = + wpabuf_dup(hapd->conf->wps_vendor_ext[i]); + if (wps->dev.vendor_ext[i] == NULL) { + while (--i >= 0) + wpabuf_free(wps->dev.vendor_ext[i]); + return -1; + } + } + + return 0; } @@ -522,11 +781,22 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->wps_state = hapd->conf->wps_state; wps->ap_setup_locked = hapd->conf->ap_setup_locked; if (is_nil_uuid(hapd->conf->uuid)) { - uuid_gen_mac_addr(hapd->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", - wps->uuid, UUID_LEN); - } else + const u8 *uuid; + uuid = get_own_uuid(hapd->iface); + if (uuid) { + os_memcpy(wps->uuid, uuid, UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " + "interface", wps->uuid, UUID_LEN); + } else { + uuid_gen_mac_addr(hapd->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " + "address", wps->uuid, UUID_LEN); + } + } else { os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", + wps->uuid, UUID_LEN); + } wps->ssid_len = hapd->conf->ssid.ssid_len; os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); wps->ap = 1; @@ -543,13 +813,30 @@ int hostapd_init_wps(struct hostapd_data *hapd, os_strdup(hapd->conf->serial_number) : NULL; wps->config_methods = wps_config_methods_str2bin(hapd->conf->config_methods); - if (hapd->conf->device_type && - wps_dev_type_str2bin(hapd->conf->device_type, - wps->dev.pri_dev_type) < 0) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); +#ifdef CONFIG_WPS2 + if ((wps->config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | + WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { + wpa_printf(MSG_INFO, "WPS: Converting display to " + "virtual_display for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; + } + if ((wps->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS: Converting push_button to " + "virtual_push_button for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ + os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) { os_free(wps); return -1; } + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ @@ -647,10 +934,13 @@ int hostapd_init_wps(struct hostapd_data *hapd, conf->skip_cred_build; if (conf->ssid.security_policy == SECURITY_STATIC_WEP) cfg.static_wep_only = 1; + cfg.dualband = interface_count(hapd->iface) > 1; + if (cfg.dualband) + wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { - printf("Failed to initialize WPS Registrar\n"); + wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); os_free(wps->network_key); os_free(wps); return -1; @@ -662,20 +952,34 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->model_description = hapd->conf->model_description; wps->model_url = hapd->conf->model_url; wps->upc = hapd->conf->upc; +#endif /* CONFIG_WPS_UPNP */ + hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + + hapd->wps = wps; + + return 0; +} + + +int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + + if (wps == NULL) + return 0; + +#ifdef CONFIG_WPS_UPNP if (hostapd_wps_upnp_init(hapd, wps) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); wps_registrar_deinit(wps->registrar); os_free(wps->network_key); os_free(wps); + hapd->wps = NULL; return -1; } #endif /* CONFIG_WPS_UPNP */ - hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); - - hapd->wps = wps; - return 0; } @@ -707,6 +1011,17 @@ void hostapd_update_wps(struct hostapd_data *hapd) { if (hapd->wps == NULL) return; + +#ifdef CONFIG_WPS_UPNP + hapd->wps->friendly_name = hapd->conf->friendly_name; + hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; + hapd->wps->model_description = hapd->conf->model_description; + hapd->wps->model_url = hapd->conf->model_url; + hapd->wps->upc = hapd->conf->upc; +#endif /* CONFIG_WPS_UPNP */ + + hostapd_wps_set_vendor_ext(hapd, hapd->wps); + if (hapd->conf->wps_state) wps_registrar_update_ie(hapd->wps->registrar); else @@ -714,29 +1029,72 @@ void hostapd_update_wps(struct hostapd_data *hapd) } -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout) +struct wps_add_pin_data { + const u8 *addr; + const u8 *uuid; + const u8 *pin; + size_t pin_len; + int timeout; + int added; +}; + + +static int wps_add_pin(struct hostapd_data *hapd, void *ctx) { - u8 u[UUID_LEN]; - int any = 0; + struct wps_add_pin_data *data = ctx; + int ret; if (hapd->wps == NULL) - return -1; + return 0; + ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, + data->uuid, data->pin, data->pin_len, + data->timeout); + if (ret == 0) + data->added++; + return ret; +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout) +{ + u8 u[UUID_LEN]; + struct wps_add_pin_data data; + + data.addr = addr; + data.uuid = u; + data.pin = (const u8 *) pin; + data.pin_len = os_strlen(pin); + data.timeout = timeout; + data.added = 0; + if (os_strcmp(uuid, "any") == 0) - any = 1; - else if (uuid_str2bin(uuid, u)) + data.uuid = NULL; + else { + if (uuid_str2bin(uuid, u)) + return -1; + data.uuid = u; + } + if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) return -1; - return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, - (const u8 *) pin, os_strlen(pin), - timeout); + return data.added ? 0 : -1; } -int hostapd_wps_button_pushed(struct hostapd_data *hapd) +static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { + const u8 *p2p_dev_addr = ctx; if (hapd->wps == NULL) - return -1; - return wps_registrar_button_pushed(hapd->wps->registrar); + return 0; + return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); +} + + +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) +{ + return hostapd_wps_for_each(hapd, wps_button_pushed, + (void *) p2p_dev_addr); } @@ -777,7 +1135,7 @@ int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && - hostapd_wps_add_pin(hapd, "any", + hostapd_wps_add_pin(hapd, NULL, "any", wpabuf_head(wps->oob_conf.dev_password), 0) < 0) goto error; @@ -794,7 +1152,8 @@ error: #endif /* CONFIG_WPS_OOB */ -static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len) { struct hostapd_data *hapd = ctx; @@ -819,15 +1178,28 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps_ie == NULL) return 0; + if (wps_validate_probe_req(wps_ie, addr) < 0) { + wpabuf_free(wps_ie); + return 0; + } if (wpabuf_len(wps_ie) > 0) { - wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); + int p2p_wildcard = 0; +#ifdef CONFIG_P2P + if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) + p2p_wildcard = 1; +#endif /* CONFIG_P2P */ + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, + p2p_wildcard); #ifdef CONFIG_WPS_UPNP /* FIX: what exactly should be included in the WLANEvent? * WPS attributes? Full ProbeReq frame? */ - upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, - UPNP_WPS_WLANEVENT_TYPE_PROBE, - wps_ie); + if (!p2p_wildcard) + upnp_wps_device_send_wlan_event( + hapd->wps_upnp, addr, + UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie); #endif /* CONFIG_WPS_UPNP */ } @@ -864,6 +1236,7 @@ static int hostapd_rx_req_put_wlan_response( */ sta = ap_get_sta(hapd, mac_addr); +#ifndef CONFIG_WPS_STRICT if (!sta) { /* * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: @@ -877,8 +1250,9 @@ static int hostapd_rx_req_put_wlan_response( break; } } +#endif /* CONFIG_WPS_STRICT */ - if (!sta) { + if (!sta || !(sta->flags & WLAN_STA_WPS)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); return 0; } @@ -911,26 +1285,19 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd, if (hapd->conf->ap_pin) ctx->ap_pin = os_strdup(hapd->conf->ap_pin); - hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); - if (hapd->wps_upnp == NULL) { - os_free(ctx); + hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd, + hapd->conf->upnp_iface); + if (hapd->wps_upnp == NULL) return -1; - } wps->wps_upnp = hapd->wps_upnp; - if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { - upnp_wps_device_deinit(hapd->wps_upnp); - hapd->wps_upnp = NULL; - return -1; - } - return 0; } static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) { - upnp_wps_device_deinit(hapd->wps_upnp); + upnp_wps_device_deinit(hapd->wps_upnp, hapd); } #endif /* CONFIG_WPS_UPNP */ @@ -950,6 +1317,7 @@ static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) struct hostapd_data *hapd = eloop_data; wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); hostapd_wps_ap_pin_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); } @@ -957,6 +1325,7 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) { wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); hapd->ap_pin_failures = 0; + hapd->ap_pin_failures_consecutive = 0; hapd->conf->ap_setup_locked = 0; if (hapd->wps->ap_setup_locked) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); @@ -970,31 +1339,53 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) } -void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) +static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) { - wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = NULL; #ifdef CONFIG_WPS_UPNP upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); #endif /* CONFIG_WPS_UPNP */ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + return 0; } -const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) { - unsigned int pin; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); +} + + +struct wps_ap_pin_data { char pin_txt[9]; + int timeout; +}; - pin = wps_generate_pin(); - os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin); + +static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) +{ + struct wps_ap_pin_data *data = ctx; os_free(hapd->conf->ap_pin); - hapd->conf->ap_pin = os_strdup(pin_txt); + hapd->conf->ap_pin = os_strdup(data->pin_txt); #ifdef CONFIG_WPS_UPNP - upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt); + upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); #endif /* CONFIG_WPS_UPNP */ - hostapd_wps_ap_pin_enable(hapd, timeout); + hostapd_wps_ap_pin_enable(hapd, data->timeout); + return 0; +} + + +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +{ + unsigned int pin; + struct wps_ap_pin_data data; + + pin = wps_generate_pin(); + os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); + data.timeout = timeout; + hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); return hapd->conf->ap_pin; } @@ -1008,13 +1399,75 @@ const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout) { - os_free(hapd->conf->ap_pin); - hapd->conf->ap_pin = os_strdup(pin); - if (hapd->conf->ap_pin == NULL) + struct wps_ap_pin_data data; + int ret; + + ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) return -1; -#ifdef CONFIG_WPS_UPNP - upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin); -#endif /* CONFIG_WPS_UPNP */ - hostapd_wps_ap_pin_enable(hapd, timeout); + data.timeout = timeout; + return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); +} + + +static int wps_update_ie(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps) + wps_registrar_update_ie(hapd->wps->registrar); return 0; } + + +void hostapd_wps_update_ie(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_update_ie, NULL); +} + + +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key) +{ + struct wps_credential cred; + size_t len; + + os_memset(&cred, 0, sizeof(cred)); + + len = os_strlen(ssid); + if ((len & 1) || len > 2 * sizeof(cred.ssid) || + hexstr2bin(ssid, cred.ssid, len / 2)) + return -1; + cred.ssid_len = len / 2; + + if (os_strncmp(auth, "OPEN", 4) == 0) + cred.auth_type = WPS_AUTH_OPEN; + else if (os_strncmp(auth, "WPAPSK", 6) == 0) + cred.auth_type = WPS_AUTH_WPAPSK; + else if (os_strncmp(auth, "WPA2PSK", 7) == 0) + cred.auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + if (encr) { + if (os_strncmp(encr, "NONE", 4) == 0) + cred.encr_type = WPS_ENCR_NONE; + else if (os_strncmp(encr, "WEP", 3) == 0) + cred.encr_type = WPS_ENCR_WEP; + else if (os_strncmp(encr, "TKIP", 4) == 0) + cred.encr_type = WPS_ENCR_TKIP; + else if (os_strncmp(encr, "CCMP", 4) == 0) + cred.encr_type = WPS_ENCR_AES; + else + return -1; + } else + cred.encr_type = WPS_ENCR_NONE; + + if (key) { + len = os_strlen(key); + if ((len & 1) || len > 2 * sizeof(cred.key) || + hexstr2bin(key, cred.key, len / 2)) + return -1; + cred.key_len = len / 2; + } + + return wps_registrar_config_ap(hapd->wps->registrar, &cred); +} diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index e978a1c..6b28c13 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -19,11 +19,13 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); +int hostapd_init_wps_complete(struct hostapd_data *hapd); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout); -int hostapd_wps_button_pushed(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout); +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr); int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, char *path, char *method, char *name); int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, @@ -33,6 +35,9 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout); const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd); int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int timeout); +void hostapd_wps_update_ie(struct hostapd_data *hapd); +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key); #else /* CONFIG_WPS */ @@ -46,6 +51,11 @@ static inline void hostapd_deinit_wps(struct hostapd_data *hapd) { } +static inline int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + return 0; +} + static inline void hostapd_update_wps(struct hostapd_data *hapd) { } @@ -57,7 +67,8 @@ static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, return 0; } -static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd) +static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) { return 0; } diff --git a/src/common/defs.h b/src/common/defs.h index 173bbd1..bfbb4b7 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -46,28 +46,34 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { - return akm == WPA_KEY_MGMT_IEEE8021X || - akm == WPA_KEY_MGMT_FT_IEEE8021X || - akm == WPA_KEY_MGMT_IEEE8021X_SHA256; + return !!(akm & (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_IEEE8021X_SHA256)); } static inline int wpa_key_mgmt_wpa_psk(int akm) { - return akm == WPA_KEY_MGMT_PSK || - akm == WPA_KEY_MGMT_FT_PSK || - akm == WPA_KEY_MGMT_PSK_SHA256; + return !!(akm & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)); } static inline int wpa_key_mgmt_ft(int akm) { - return akm == WPA_KEY_MGMT_FT_PSK || - akm == WPA_KEY_MGMT_FT_IEEE8021X; + return !!(akm & (WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X)); } static inline int wpa_key_mgmt_sha256(int akm) { - return akm == WPA_KEY_MGMT_PSK_SHA256 || - akm == WPA_KEY_MGMT_IEEE8021X_SHA256; + return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)); +} + +static inline int wpa_key_mgmt_wpa(int akm) +{ + return wpa_key_mgmt_wpa_ieee8021x(akm) || + wpa_key_mgmt_wpa_psk(akm); } @@ -137,6 +143,15 @@ enum wpa_states { WPA_DISCONNECTED, /** + * WPA_INTERFACE_DISABLED - Interface disabled + * + * This stat eis entered if the network interface is disabled, e.g., + * due to rfkill. wpa_supplicant refuses any new operations that would + * use the radio until the interface has been enabled. + */ + WPA_INTERFACE_DISABLED, + + /** * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) * * This state is entered if there are no enabled networks in the @@ -252,4 +267,18 @@ enum hostapd_hw_mode { NUM_HOSTAPD_MODES }; +/** + * enum wpa_ctrl_req_type - Control interface request types + */ +enum wpa_ctrl_req_type { + WPA_CTRL_REQ_UNKNOWN, + WPA_CTRL_REQ_EAP_IDENTITY, + WPA_CTRL_REQ_EAP_PASSWORD, + WPA_CTRL_REQ_EAP_NEW_PASSWORD, + WPA_CTRL_REQ_EAP_PIN, + WPA_CTRL_REQ_EAP_OTP, + WPA_CTRL_REQ_EAP_PASSPHRASE, + NUM_WPA_CTRL_REQS +}; + #endif /* DEFS_H */ diff --git a/src/common/gas.c b/src/common/gas.c new file mode 100644 index 0000000..babdaa3 --- /dev/null +++ b/src/common/gas.c @@ -0,0 +1,279 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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 "ieee802_11_defs.h" +#include "gas.h" + + +static struct wpabuf * +gas_build_req(u8 action, u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + + return buf; +} + + +static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +{ + return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, + size); +} + + +struct wpabuf * gas_build_comeback_req(u8 dialog_token) +{ + return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); +} + + +static struct wpabuf * +gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, + u8 more, u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, status_code); + if (action == WLAN_PA_GAS_COMEBACK_RESP) + wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); + wpabuf_put_le16(buf, comeback_delay); + + return buf; +} + + +struct wpabuf * +gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, + size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, + status_code, 0, 0, comeback_delay, size); +} + + +static struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, + status_code, frag_id, more, comeback_delay, + size); +} + + +/** + * gas_add_adv_proto_anqp - Add an Advertisement Protocol element + * @buf: Buffer to which the element is added + * @query_resp_len_limit: Query Response Length Limit in units of 256 octets + * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) + * + * + * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means + * that the maximum limit is determined by the maximum allowable number of + * fragments in the GAS Query Response Fragment ID. + */ +static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, + u8 pame_bi) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 2); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + /* Advertisement Protocol */ + wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); +} + + +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_req(dialog_token, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0, 0); + + wpabuf_put(buf, 2); /* Query Request Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, + 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_comeback_resp(dialog_token, status_code, + frag_id, more, comeback_delay, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +/** + * gas_anqp_set_len - Set Query Request/Response Length + * @buf: GAS message + * + * This function is used to update the Query Request/Response Length field once + * the payload has been filled. + */ +void gas_anqp_set_len(struct wpabuf *buf) +{ + u8 action; + size_t offset; + u8 *len; + + if (buf == NULL || wpabuf_len(buf) < 2) + return; + + action = *(wpabuf_head_u8(buf) + 1); + switch (action) { + case WLAN_PA_GAS_INITIAL_REQ: + offset = 3 + 4; + break; + case WLAN_PA_GAS_INITIAL_RESP: + offset = 7 + 4; + break; + case WLAN_PA_GAS_COMEBACK_RESP: + offset = 8 + 4; + break; + default: + return; + } + + if (wpabuf_len(buf) < offset + 2) + return; + + len = wpabuf_mhead_u8(buf) + offset; + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +/** + * gas_anqp_add_element - Add ANQP element header + * @buf: GAS message + * @info_id: ANQP Info ID + * Returns: Pointer to the Length field for gas_anqp_set_element_len() + */ +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) +{ + wpabuf_put_le16(buf, info_id); + return wpabuf_put(buf, 2); /* Length to be filled */ +} + + +/** + * gas_anqp_set_element_len - Update ANQP element Length field + * @buf: GAS message + * @len_pos: Length field position from gas_anqp_add_element() + * + * This function is called after the ANQP element payload has been added to the + * buffer. + */ +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) +{ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); +} diff --git a/src/common/gas.h b/src/common/gas.h new file mode 100644 index 0000000..2f8d2cb --- /dev/null +++ b/src/common/gas.h @@ -0,0 +1,42 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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. + */ + +#ifndef GAS_H +#define GAS_H + +struct wpabuf * gas_build_comeback_req(u8 dialog_token); +struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload); +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload); +void gas_anqp_set_len(struct wpabuf *buf); + +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); + +#endif /* GAS_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 96ef5b6..43cb2c6 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -75,7 +75,7 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->wmm_tspec_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "unknown WMM " + wpa_printf(MSG_EXCESSIVE, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); @@ -88,7 +88,23 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->wps_ie_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Microsoft " + wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_WFA: + switch (pos[3]) { + case P2P_OUI_TYPE: + /* Wi-Fi Alliance - P2P IE */ + elems->p2p = pos; + elems->p2p_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " "(type=%d len=%lu)\n", pos[3], (unsigned long) elen); @@ -103,18 +119,18 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->vendor_ht_cap_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Broadcom " + wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " - "(type=%d len=%lu)\n", + "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } break; default: - wpa_printf(MSG_MSGDUMP, "unknown vendor specific information " - "element ignored (vendor OUI %02x:%02x:%02x " - "len=%lu)", + wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " + "information element ignored (vendor OUI " + "%02x:%02x:%02x len=%lu)", pos[0], pos[1], pos[2], (unsigned long) elen); return -1; } @@ -238,6 +254,15 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_LINK_ID: + if (elen < 18) + break; + elems->link_id = pos; + break; + case WLAN_EID_INTERWORKING: + elems->interworking = pos; + elems->interworking_len = elen; + break; default: unknown++; if (!show_errors) @@ -324,3 +349,43 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, return buf; } + + +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) +{ + u16 fc, type, stype; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return NULL; + + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return NULL; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS | WLAN_FC_TODS: + case WLAN_FC_TODS: + return hdr->addr1; + case WLAN_FC_FROMDS: + return hdr->addr2; + default: + return NULL; + } + case WLAN_FC_TYPE_CTRL: + if (stype != WLAN_FC_STYPE_PSPOLL) + return NULL; + return hdr->addr1; + case WLAN_FC_TYPE_MGMT: + return hdr->addr3; + default: + return NULL; + } +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 4a4f5a7..60f0974 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -40,6 +40,9 @@ struct ieee802_11_elems { const u8 *ht_capabilities; const u8 *ht_operation; const u8 *vendor_ht_cap; + const u8 *p2p; + const u8 *link_id; + const u8 *interworking; u8 ssid_len; u8 supp_rates_len; @@ -64,6 +67,8 @@ struct ieee802_11_elems { u8 ht_capabilities_len; u8 ht_operation_len; u8 vendor_ht_cap_len; + u8 p2p_len; + u8 interworking_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -74,5 +79,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, int ieee802_11_ie_count(const u8 *ies, size_t ies_len); struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type); +struct ieee80211_hdr; +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 4881e39..4cbc535 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -71,6 +71,12 @@ #define WLAN_FC_STYPE_CFPOLL 6 #define WLAN_FC_STYPE_CFACKPOLL 7 #define WLAN_FC_STYPE_QOS_DATA 8 +#define WLAN_FC_STYPE_QOS_DATA_CFACK 9 +#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 +#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 +#define WLAN_FC_STYPE_QOS_NULL 12 +#define WLAN_FC_STYPE_QOS_CFPOLL 14 +#define WLAN_FC_STYPE_QOS_CFACKPOLL 15 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 @@ -95,6 +101,11 @@ /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 +#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 +#define WLAN_STATUS_SECURITY_DISABLED 5 +#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 +#define WLAN_STATUS_NOT_IN_SAME_BSS 7 #define WLAN_STATUS_CAPS_UNSUPPORTED 10 #define WLAN_STATUS_REASSOC_NO_ASSOC 11 #define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 @@ -114,9 +125,10 @@ #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 /* IEEE 802.11g */ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 -#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 -#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 +#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 /* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 @@ -141,6 +153,17 @@ #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 +#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 +#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 +#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 +#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 +#define WLAN_STATUS_REQ_REFUSED_HOME 64 +#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 +#define WLAN_STATUS_REQ_REFUSED_SSPN 67 +#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 +#define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_TRANSMISSION_FAILURE 79 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -168,6 +191,10 @@ #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 +#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 +#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 +/* IEEE 802.11e */ +#define WLAN_REASON_DISASSOC_LOW_ACK 34 /* Information Element IDs */ @@ -202,10 +229,17 @@ #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 +#define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_LINK_ID 101 +#define WLAN_EID_INTERWORKING 107 +#define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_EXT_CAPAB 127 #define WLAN_EID_VENDOR_SPECIFIC 221 @@ -219,7 +253,19 @@ #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_WNM 10 +#define WLAN_ACTION_UNPROTECTED_WNM 11 +#define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_VENDOR_SPECIFIC 127 + +/* Public action codes */ +#define WLAN_PA_VENDOR_SPECIFIC 9 +#define WLAN_PA_GAS_INITIAL_REQ 10 +#define WLAN_PA_GAS_INITIAL_RESP 11 +#define WLAN_PA_GAS_COMEBACK_REQ 12 +#define WLAN_PA_GAS_COMEBACK_RESP 13 +#define WLAN_TDLS_DISCOVERY_RESPONSE 14 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 @@ -227,11 +273,99 @@ #define WLAN_SA_QUERY_TR_ID_LEN 2 +/* TDLS action codes */ +#define WLAN_TDLS_SETUP_REQUEST 0 +#define WLAN_TDLS_SETUP_RESPONSE 1 +#define WLAN_TDLS_SETUP_CONFIRM 2 +#define WLAN_TDLS_TEARDOWN 3 +#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4 +#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5 +#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6 +#define WLAN_TDLS_PEER_PSM_REQUEST 7 +#define WLAN_TDLS_PEER_PSM_RESPONSE 8 +#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 +#define WLAN_TDLS_DISCOVERY_REQUEST 10 + /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 #define WLAN_TIMEOUT_ASSOC_COMEBACK 3 +/* Interworking element (IEEE 802.11u) - Access Network Options */ +#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f +#define INTERWORKING_ANO_INTERNET 0x10 +#define INTERWORKING_ANO_ASRA 0x20 +#define INTERWORKING_ANO_ESR 0x40 +#define INTERWORKING_ANO_UESA 0x80 + +#define INTERWORKING_ANT_PRIVATE 0 +#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 +#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 +#define INTERWORKING_ANT_FREE_PUBLIC 3 +#define INTERWORKING_ANT_PERSONAL_DEVICE 4 +#define INTERWORKING_ANT_EMERGENCY_SERVICES 5 +#define INTERWORKING_ANT_TEST 6 +#define INTERWORKING_ANT_WILDCARD 15 + +/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +enum adv_proto_id { + ACCESS_NETWORK_QUERY_PROTOCOL = 0, + MIH_INFO_SERVICE = 1, + MIH_CMD_AND_EVENT_DISCOVERY = 2, + EMERGENCY_ALERT_SYSTEM = 3, + ADV_PROTO_VENDOR_SPECIFIC = 221 +}; + +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +enum anqp_info_id { + ANQP_QUERY_LIST = 256, + ANQP_CAPABILITY_LIST = 257, + ANQP_VENUE_NAME = 258, + ANQP_EMERGENCY_CALL_NUMBER = 259, + ANQP_NETWORK_AUTH_TYPE = 260, + ANQP_ROAMING_CONSORTIUM = 261, + ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, + ANQP_NAI_REALM = 263, + ANQP_3GPP_CELLULAR_NETWORK = 264, + ANQP_AP_GEOSPATIAL_LOCATION = 265, + ANQP_AP_CIVIC_LOCATION = 266, + ANQP_AP_LOCATION_PUBLIC_URI = 267, + ANQP_DOMAIN_NAME = 268, + ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_EMERGENCY_NAI = 271, + ANQP_VENDOR_SPECIFIC = 56797 +}; + +/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ +enum nai_realm_eap_auth_param { + NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, + NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, + NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, + NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, + NAI_REALM_EAP_AUTH_CRED_TYPE = 5, + NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, + NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 +}; + +enum nai_realm_eap_auth_inner_non_eap { + NAI_REALM_INNER_NON_EAP_PAP = 1, + NAI_REALM_INNER_NON_EAP_CHAP = 2, + NAI_REALM_INNER_NON_EAP_MSCHAP = 3, + NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 +}; + +enum nai_realm_eap_cred_type { + NAI_REALM_CRED_TYPE_SIM = 1, + NAI_REALM_CRED_TYPE_USIM = 2, + NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, + NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, + NAI_REALM_CRED_TYPE_SOFTOKEN = 5, + NAI_REALM_CRED_TYPE_CERTIFICATE = 6, + NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, + NAI_REALM_CRED_TYPE_NONE = 8, + NAI_REALM_CRED_TYPE_ANONYMOUS = 9, + NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 +}; #ifdef _MSC_VER #pragma pack(push, 1) @@ -273,6 +407,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED auth; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED deauth; struct { le16 capab_info; @@ -296,6 +431,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED reassoc_req; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -355,6 +491,28 @@ struct ieee80211_mgmt { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } STRUCT_PACKED sa_query_resp; + struct { + u8 action; + u8 variable[0]; + } STRUCT_PACKED public_action; + struct { + u8 action; /* 9 */ + u8 oui[3]; + /* Vendor-specific content */ + u8 variable[0]; + } STRUCT_PACKED vs_public_action; + struct { + u8 action; /* 7 */ + u8 dialog_token; + u8 req_mode; + le16 disassoc_timer; + u8 validity_interval; + /* BSS Termination Duration (optional), + * Session Information URL (optional), + * BSS Transition Candidate List + * Entries */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_req; } u; } STRUCT_PACKED action; } u; @@ -473,11 +631,14 @@ struct ieee80211_ht_operation { #define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201 #define WPS_IE_VENDOR_TYPE 0x0050f204 +#define OUI_WFA 0x506f9a +#define P2P_IE_VENDOR_TYPE 0x506f9a09 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -544,7 +705,7 @@ struct wmm_parameter_element { u8 oui_type; /* 2 */ u8 oui_subtype; /* 1 */ u8 version; /* 1 for WMM version 1.0 */ - u8 qos_info; /* AP/STA specif QoS info */ + u8 qos_info; /* AP/STA specific QoS info */ u8 reserved; /* 0 */ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ @@ -587,6 +748,115 @@ enum { }; +/* Wi-Fi Direct (P2P) */ + +#define P2P_OUI_TYPE 9 + +enum p2p_attr_id { + P2P_ATTR_STATUS = 0, + P2P_ATTR_MINOR_REASON_CODE = 1, + P2P_ATTR_CAPABILITY = 2, + P2P_ATTR_DEVICE_ID = 3, + P2P_ATTR_GROUP_OWNER_INTENT = 4, + P2P_ATTR_CONFIGURATION_TIMEOUT = 5, + P2P_ATTR_LISTEN_CHANNEL = 6, + P2P_ATTR_GROUP_BSSID = 7, + P2P_ATTR_EXT_LISTEN_TIMING = 8, + P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, + P2P_ATTR_MANAGEABILITY = 10, + P2P_ATTR_CHANNEL_LIST = 11, + P2P_ATTR_NOTICE_OF_ABSENCE = 12, + P2P_ATTR_DEVICE_INFO = 13, + P2P_ATTR_GROUP_INFO = 14, + P2P_ATTR_GROUP_ID = 15, + P2P_ATTR_INTERFACE = 16, + P2P_ATTR_OPERATING_CHANNEL = 17, + P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_VENDOR_SPECIFIC = 221 +}; + +#define P2P_MAX_GO_INTENT 15 + +/* P2P Capability - Device Capability bitmap */ +#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) +#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) +#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) +#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) +#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) +#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) + +/* P2P Capability - Group Capability bitmap */ +#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) +#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) +#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) +#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) +#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) +#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) +#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) + +/* Invitation Flags */ +#define P2P_INVITATION_FLAGS_TYPE BIT(0) + +/* P2P Manageability */ +#define P2P_MAN_DEVICE_MANAGEMENT BIT(0) +#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1) +#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2) + +enum p2p_status_code { + P2P_SC_SUCCESS = 0, + P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1, + P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2, + P2P_SC_FAIL_LIMIT_REACHED = 3, + P2P_SC_FAIL_INVALID_PARAMS = 4, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5, + P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6, + P2P_SC_FAIL_NO_COMMON_CHANNELS = 7, + P2P_SC_FAIL_UNKNOWN_GROUP = 8, + P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, + P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, + P2P_SC_FAIL_REJECTED_BY_USER = 11, +}; + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_WILDCARD_SSID_LEN 7 + +/* P2P action frames */ +enum p2p_act_frame_type { + P2P_NOA = 0, + P2P_PRESENCE_REQ = 1, + P2P_PRESENCE_RESP = 2, + P2P_GO_DISC_REQ = 3 +}; + +/* P2P public action frames */ +enum p2p_action_frame_type { + P2P_GO_NEG_REQ = 0, + P2P_GO_NEG_RESP = 1, + P2P_GO_NEG_CONF = 2, + P2P_INVITATION_REQ = 3, + P2P_INVITATION_RESP = 4, + P2P_DEV_DISC_REQ = 5, + P2P_DEV_DISC_RESP = 6, + P2P_PROV_DISC_REQ = 7, + P2P_PROV_DISC_RESP = 8 +}; + +enum p2p_service_protocol_type { + P2P_SERV_ALL_SERVICES = 0, + P2P_SERV_BONJOUR = 1, + P2P_SERV_UPNP = 2, + P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_VENDOR_SPECIFIC = 255 +}; + +enum p2p_sd_status { + P2P_SD_SUCCESS = 0, + P2P_SD_PROTO_NOT_AVAILABLE = 1, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, + P2P_SD_BAD_REQUEST = 3 +}; + + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ @@ -604,4 +874,44 @@ enum { #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 + +/* IEEE 802.11v - WNM Action field values */ +enum wnm_action { + WNM_EVENT_REQ = 0, + WNM_EVENT_REPORT = 1, + WNM_DIAGNOSTIC_REQ = 2, + WNM_DIAGNOSTIC_REPORT = 3, + WNM_LOCATION_CFG_REQ = 4, + WNM_LOCATION_CFG_RESP = 5, + WNM_BSS_TRANS_MGMT_QUERY = 6, + WNM_BSS_TRANS_MGMT_REQ = 7, + WNM_BSS_TRANS_MGMT_RESP = 8, + WNM_FMS_REQ = 9, + WNM_FMS_RESP = 10, + WNM_COLLOCATED_INTERFERENCE_REQ = 11, + WNM_COLLOCATED_INTERFERENCE_REPORT = 12, + WNM_TFS_REQ = 13, + WNM_TFS_RESP = 14, + WNM_TFS_NOTIFY = 15, + WNM_SLEEP_MODE_REQ = 16, + WNM_SLEEP_MODE_RESP = 17, + WNM_TIM_BROADCAST_REQ = 18, + WNM_TIM_BROADCAST_RESP = 19, + WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, + WNM_CHANNEL_USAGE_REQ = 21, + WNM_CHANNEL_USAGE_RESP = 22, + WNM_DMS_REQ = 23, + WNM_DMS_RESP = 24, + WNM_TIMING_MEASUREMENT_REQ = 25, + WNM_NOTIFICATION_REQ = 26, + WNM_NOTIFICATION_RESP = 27 +}; + +/* IEEE 802.11v - BSS Transition Management Request - Request Mode */ +#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) +#define WNM_BSS_TM_REQ_ABRIDGED BIT(1) +#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) +#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) +#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/version.h b/src/common/version.h index 02f34be..d3d6915 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -1,6 +1,10 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.7.3" +#ifndef VERSION_STR_POSTFIX +#define VERSION_STR_POSTFIX "" +#endif /* VERSION_STR_POSTFIX */ + +#define VERSION_STR "1.0" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index b295f31..24a61e4 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -126,6 +126,8 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); } @@ -186,6 +188,154 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, return 0; } + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; +#ifdef CONFIG_IEEE80211W + case FTIE_SUBELEM_IGTK: + parse->igtk = pos + 2; + parse->igtk_len = pos[1]; + break; +#endif /* CONFIG_IEEE80211W */ + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (pos[1] < sizeof(*ftie)) + return -1; + ftie = (const struct rsn_ftie *) (pos + 2); + prot_ie_count = ftie->mic_control[1]; + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + parse->tie = pos + 2; + parse->tie_len = pos[1]; + break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; + break; + } + + pos += 2 + pos[1]; + } + + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + + return 0; +} #endif /* CONFIG_IEEE80211R */ @@ -403,6 +553,144 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_WPA; + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + #ifdef CONFIG_IEEE80211R /** diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index fd8a79f..69437a7 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -56,6 +56,7 @@ #endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -68,6 +69,7 @@ #ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -115,7 +117,13 @@ /* B4-B5: GTKSA Replay Counter */ #define WPA_CAPABILITY_MFPR BIT(6) #define WPA_CAPABILITY_MFPC BIT(7) +/* B8: Reserved */ #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) +#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10) +#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) +#define WPA_CAPABILITY_PBAC BIT(12) +#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) +/* B14-B15: Reserved */ /* IEEE 802.11r */ @@ -337,6 +345,8 @@ struct wpa_ie_data { int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256); @@ -348,4 +358,27 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie2, size_t ie2len); int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *tie; + size_t tie_len; + const u8 *igtk; + size_t igtk_len; + const u8 *ric; + size_t ric_len; +}; + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); + #endif /* WPA_COMMON_H */ diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 2b4e3aa..3b25f77 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -20,6 +20,12 @@ #include #endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef ANDROID +#include +#include +#include "private/android_filesystem_config.h" +#endif /* ANDROID */ + #include "wpa_ctrl.h" #include "common.h" @@ -58,6 +64,14 @@ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_UNIX +#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR +#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" +#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ +#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX +#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" +#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ + + struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; @@ -81,7 +95,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) counter++; try_again: ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter); + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { close(ctrl->s); os_free(ctrl); @@ -105,6 +121,31 @@ try_again: return NULL; } +#ifdef ANDROID + chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + /* + * If the ctrl_path isn't an absolute pathname, assume that + * it's the name of a socket in the Android reserved namespace. + * Otherwise, it's a normal UNIX domain socket appearing in the + * filesystem. + */ + if (ctrl_path != NULL && *ctrl_path != '/') { + char buf[21]; + os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); + if (socket_local_client_connect( + ctrl->s, buf, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } +#endif /* ANDROID */ + ctrl->dest.sun_family = AF_UNIX; res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path)); @@ -127,11 +168,64 @@ try_again: void wpa_ctrl_close(struct wpa_ctrl *ctrl) { + if (ctrl == NULL) + return; unlink(ctrl->local.sun_path); - close(ctrl->s); + if (ctrl->s >= 0) + close(ctrl->s); os_free(ctrl); } + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void) +{ + DIR *dir; + struct dirent entry; + struct dirent *result; + size_t dirnamelen; + int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX); + size_t maxcopy; + char pathname[PATH_MAX]; + char *namep; + + if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) + return; + + dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", + CONFIG_CTRL_IFACE_CLIENT_DIR); + if (dirnamelen >= sizeof(pathname)) { + closedir(dir); + return; + } + namep = pathname + dirnamelen; + maxcopy = PATH_MAX - dirnamelen; + while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { + if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX, + prefixlen) == 0) { + if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) + unlink(pathname); + } + } + closedir(dir); +} +#endif /* ANDROID */ + +#else /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef ANDROID +void wpa_ctrl_cleanup(void) +{ +} +#endif /* ANDROID */ + #endif /* CONFIG_CTRL_IFACE_UNIX */ @@ -234,11 +328,13 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, os_free(cmd_buf); for (;;) { - tv.tv_sec = 2; + tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0) + return res; if (FD_ISSET(ctrl->s, &rfds)) { res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index d770fd4..d13ba02 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -32,6 +32,8 @@ extern "C" { #define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " /** Disconnected, data connection is not available */ #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** Association rejected during connection attempt */ +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -54,6 +56,8 @@ extern "C" { #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** wpa_supplicant state change */ +#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " /** A new BSS entry was added (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " /** A BSS entry was removed (followed by BSS entry id and BSSID) */ @@ -63,6 +67,8 @@ extern "C" { #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " /** Available WPS AP with active PBC found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " /** Available WPS AP with recently selected PIN registrar found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " @@ -81,11 +87,51 @@ extern "C" { #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " + /* WPS ER events */ #define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " #define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " #define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " #define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " +#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " +#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " + +/** P2P device found */ +#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " + +/** P2P device lost */ +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " + +/** A P2P device requested GO negotiation, but we were not ready to start the + * negotiation */ +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: */ +#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " +/* parameters: */ +#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " +#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " +#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " + +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " @@ -223,6 +269,17 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void); +#endif /* ANDROID */ + #ifdef CONFIG_CTRL_IFACE_UDP #define WPA_CTRL_IFACE_PORT 9877 #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 diff --git a/src/crypto/Makefile b/src/crypto/Makefile index 69aa16a..0454827 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -48,6 +48,7 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +LIB_OBJS += random.o libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index 8fdba65..5f715a8 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -63,7 +63,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; MD5Init(&ctx->u.md5); @@ -81,7 +82,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); for (i = 0; i < sizeof(k_pad); i++) k_pad[i] ^= 0x36; SHA1Init(&ctx->u.sha1); diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index 7bd2fb7..e5b7d4c 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -16,6 +16,7 @@ #include "common.h" #include "crypto.h" +#include "random.h" #include "dh_groups.h" @@ -564,7 +565,8 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { + if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) + { wpabuf_free(*priv); *priv = NULL; return NULL; diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c index a85cb14..1e0c453 100644 --- a/src/crypto/fips_prf_internal.c +++ b/src/crypto/fips_prf_internal.c @@ -28,13 +28,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) u8 *xpos = x; u32 carry; - if (seed_len > sizeof(xkey)) + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c index f8692a9..137ad91 100644 --- a/src/crypto/md5-internal.c +++ b/src/crypto/md5-internal.c @@ -188,7 +188,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); os_memcpy(digest, ctx->buf, 16); - os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + os_memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index dae15ab..c439ae9 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -19,6 +19,60 @@ #include "ms_funcs.h" #include "crypto.h" +/** + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding + * @utf8_string: UTF-8 string (IN) + * @utf8_string_len: Length of utf8_string (IN) + * @ucs2_buffer: UCS-2 buffer (OUT) + * @ucs2_buffer_size: Length of UCS-2 buffer (IN) + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string + * Returns: 0 on success, -1 on failure + */ +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, + u8 *ucs2_buffer, size_t ucs2_buffer_size, + size_t *ucs2_string_size) +{ + size_t i, j; + + for (i = 0, j = 0; i < utf8_string_len; i++) { + u8 c = utf8_string[i]; + if (j >= ucs2_buffer_size) { + /* input too long */ + return -1; + } + if (c <= 0x7F) { + WPA_PUT_LE16(ucs2_buffer + j, c); + j += 2; + } else if (i == utf8_string_len - 1 || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + u8 c2 = utf8_string[++i]; + if ((c & 0xE0) == 0xC0) { + /* two-byte encoding */ + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0x1F) << 6) | (c2 & 0x3F)); + j += 2; + } else if (i == utf8_string_len || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + /* three-byte encoding */ + u8 c3 = utf8_string[++i]; + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0xF) << 12) | + ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + } + } + } + + if (ucs2_string_size) + *ucs2_string_size = j / 2; + return 0; +} + /** * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 @@ -53,7 +107,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, /** * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) * Returns: 0 on success, -1 on failure @@ -62,18 +116,13 @@ int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash) { u8 buf[512], *pos; - size_t i, len; + size_t len, max_len; - if (password_len > 256) - password_len = 256; - - /* Convert password into unicode */ - for (i = 0; i < password_len; i++) { - buf[2 * i] = password[i]; - buf[2 * i + 1] = 0; - } + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; - len = password_len * 2; + len *= 2; pos = buf; return md4_vector(1, (const u8 **) &pos, &len, password_hash); } @@ -117,7 +166,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, * @peer_challenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure @@ -225,7 +274,7 @@ int generate_authenticator_response_pwhash( /** * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) @@ -254,7 +303,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len, /** * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 * @challenge: 8-octet Challenge (IN) - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) * Returns: 0 on success, -1 on failure @@ -375,7 +424,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key, /** * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) @@ -385,18 +434,23 @@ int encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block) { - size_t i, offset; + size_t ucs2_len, offset; u8 *pos; - if (password_len > 256) + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) return -1; - os_memset(pw_block, 0, PWBLOCK_LEN); - offset = (256 - password_len) * 2; - if (os_get_random(pw_block, offset) < 0) + if (ucs2_len > 256) return -1; - for (i = 0; i < password_len; i++) - pw_block[offset + i * 2] = password[i]; + + offset = (256 - ucs2_len) * 2; + if (offset != 0) { + os_memmove(pw_block + offset, pw_block, ucs2_len * 2); + if (os_get_random(pw_block, offset) < 0) + return -1; + } /* * PasswordLength is 4 octets, but since the maximum password length is * 256, only first two (in little endian byte order) can be non-zero. @@ -410,9 +464,9 @@ int encrypt_pw_block_with_password_hash( /** * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) * Returns: 0 on success, -1 on failure @@ -450,9 +504,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash, /** * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) * Returns: 0 on success, -1 on failure diff --git a/src/crypto/random.c b/src/crypto/random.c new file mode 100644 index 0000000..a54e197 --- /dev/null +++ b/src/crypto/random.c @@ -0,0 +1,430 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen + * + * 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 random number generator is used to provide additional entropy to the + * one provided by the operating system (os_get_random()) for session key + * generation. The os_get_random() output is expected to be secure and the + * implementation here is expected to provide only limited protection against + * cases where os_get_random() cannot provide strong randomness. This + * implementation shall not be assumed to be secure as the sole source of + * randomness. The random_get_bytes() function mixes in randomness from + * os_get_random() and as such, calls to os_get_random() can be replaced with + * calls to random_get_bytes() without reducing security. + * + * The design here follows partially the design used in the Linux + * drivers/char/random.c, but the implementation here is simpler and not as + * strong. This is a compromise to reduce duplicated CPU effort and to avoid + * extra code/memory size. As pointed out above, os_get_random() needs to be + * guaranteed to be secure for any of the security assumptions to hold. + */ + +#include "utils/includes.h" +#ifdef __linux__ +#include +#endif /* __linux__ */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "sha1.h" +#include "random.h" + +#define POOL_WORDS 32 +#define POOL_WORDS_MASK (POOL_WORDS - 1) +#define POOL_TAP1 26 +#define POOL_TAP2 20 +#define POOL_TAP3 14 +#define POOL_TAP4 7 +#define POOL_TAP5 1 +#define EXTRACT_LEN 16 +#define MIN_READY_MARK 2 + +static u32 pool[POOL_WORDS]; +static unsigned int input_rotate = 0; +static unsigned int pool_pos = 0; +static u8 dummy_key[20]; +#ifdef __linux__ +static size_t dummy_key_avail = 0; +static int random_fd = -1; +#endif /* __linux__ */ +static unsigned int own_pool_ready = 0; +#define RANDOM_ENTROPY_SIZE 20 +static char *random_entropy_file = NULL; +static int random_entropy_file_read = 0; + +#define MIN_COLLECT_ENTROPY 1000 +static unsigned int entropy = 0; +static unsigned int total_collected = 0; + + +static void random_write_entropy(void); + + +static u32 __ROL32(u32 x, u32 y) +{ + return (x << (y & 31)) | (x >> (32 - (y & 31))); +} + + +static void random_mix_pool(const void *buf, size_t len) +{ + static const u32 twist[8] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 + }; + const u8 *pos = buf; + u32 w; + + wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len); + + while (len--) { + w = __ROL32(*pos++, input_rotate & 31); + input_rotate += pool_pos ? 7 : 14; + pool_pos = (pool_pos - 1) & POOL_WORDS_MASK; + w ^= pool[pool_pos]; + w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK]; + pool[pool_pos] = (w >> 3) ^ twist[w & 7]; + } +} + + +static void random_extract(u8 *out) +{ + unsigned int i; + u8 hash[SHA1_MAC_LEN]; + u32 *hash_ptr; + u32 buf[POOL_WORDS / 2]; + + /* First, add hash back to pool to make backtracking more difficult. */ + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool, + sizeof(pool), hash); + random_mix_pool(hash, sizeof(hash)); + /* Hash half the pool to extra data */ + for (i = 0; i < POOL_WORDS / 2; i++) + buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK]; + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf, + sizeof(buf), hash); + /* + * Fold the hash to further reduce any potential output pattern. + * Though, compromise this to reduce CPU use for the most common output + * length (32) and return 16 bytes from instead of only half. + */ + hash_ptr = (u32 *) hash; + hash_ptr[0] ^= hash_ptr[4]; + os_memcpy(out, hash, EXTRACT_LEN); +} + + +void random_add_randomness(const void *buf, size_t len) +{ + struct os_time t; + static unsigned int count = 0; + + count++; + wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u", + count, entropy); + if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { + /* + * No need to add more entropy at this point, so save CPU and + * skip the update. + */ + return; + } + + os_get_time(&t); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + random_mix_pool(&t, sizeof(t)); + random_mix_pool(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + entropy++; + total_collected++; +} + + +int random_get_bytes(void *buf, size_t len) +{ + int ret; + u8 *bytes = buf; + size_t left; + + wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u", + (unsigned int) len, entropy); + + /* Start with assumed strong randomness from OS */ + ret = os_get_random(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random", + buf, len); + + /* Mix in additional entropy extracted from the internal pool */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + random_extract(tmp); + wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); + + if (entropy < len) + entropy = 0; + else + entropy -= len; + + return ret; +} + + +int random_pool_ready(void) +{ +#ifdef __linux__ + int fd; + ssize_t res; + + /* + * Make sure that there is reasonable entropy available before allowing + * some key derivation operations to proceed. + */ + + if (dummy_key_avail == sizeof(dummy_key)) + return 1; /* Already initialized - good to continue */ + + /* + * Try to fetch some more data from the kernel high quality + * /dev/random. There may not be enough data available at this point, + * so use non-blocking read to avoid blocking the application + * completely. + */ + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + res = 0; + } + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " + "/dev/random", (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + close(fd); + + if (dummy_key_avail == sizeof(dummy_key)) { + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + return 1; + } + + wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " + "random data available from /dev/random", + (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); + + if (own_pool_ready >= MIN_READY_MARK || + total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) { + wpa_printf(MSG_INFO, "random: Allow operation to proceed " + "based on internal entropy"); + return 1; + } + + wpa_printf(MSG_INFO, "random: Not enough entropy pool available for " + "secure operations"); + return 0; +#else /* __linux__ */ + /* TODO: could do similar checks on non-Linux platforms */ + return 1; +#endif /* __linux__ */ +} + + +void random_mark_pool_ready(void) +{ + own_pool_ready++; + wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be " + "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK); + random_write_entropy(); +} + + +#ifdef __linux__ + +static void random_close_fd(void) +{ + if (random_fd >= 0) { + eloop_unregister_read_sock(random_fd); + close(random_fd); + random_fd = -1; + } +} + + +static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx) +{ + ssize_t res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + return; + } + + res = read(sock, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + return; + } + + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random", + (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + } +} + +#endif /* __linux__ */ + + +static void random_read_entropy(void) +{ + char *buf; + size_t len; + + if (!random_entropy_file) + return; + + buf = os_readfile(random_entropy_file, &len); + if (buf == NULL) + return; /* entropy file not yet available */ + + if (len != 1 + RANDOM_ENTROPY_SIZE) { + wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s", + random_entropy_file); + os_free(buf); + return; + } + + own_pool_ready = (u8) buf[0]; + random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); + random_entropy_file_read = 1; + os_free(buf); + wpa_printf(MSG_DEBUG, "random: Added entropy from %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +static void random_write_entropy(void) +{ + char buf[RANDOM_ENTROPY_SIZE]; + FILE *f; + u8 opr; + int fail = 0; + + if (!random_entropy_file) + return; + + if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0) + return; + + f = fopen(random_entropy_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "random: Could not open entropy file %s " + "for writing", random_entropy_file); + return; + } + + opr = own_pool_ready > 0xff ? 0xff : own_pool_ready; + if (fwrite(&opr, 1, 1, f) != 1 || + fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1) + fail = 1; + fclose(f); + if (fail) { + wpa_printf(MSG_ERROR, "random: Could not write entropy data " + "to %s", random_entropy_file); + return; + } + + wpa_printf(MSG_DEBUG, "random: Updated entropy file %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +void random_init(const char *entropy_file) +{ + os_free(random_entropy_file); + if (entropy_file) + random_entropy_file = os_strdup(entropy_file); + else + random_entropy_file = NULL; + random_read_entropy(); + +#ifdef __linux__ + if (random_fd >= 0) + return; + + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (random_fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return; + } + wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " + "/dev/random"); + + eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL); +#endif /* __linux__ */ + + random_write_entropy(); +} + + +void random_deinit(void) +{ +#ifdef __linux__ + random_close_fd(); +#endif /* __linux__ */ + random_write_entropy(); + os_free(random_entropy_file); + random_entropy_file = NULL; +} diff --git a/src/crypto/random.h b/src/crypto/random.h new file mode 100644 index 0000000..1048bb4 --- /dev/null +++ b/src/crypto/random.h @@ -0,0 +1,34 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen + * + * 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. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#ifdef CONFIG_NO_RANDOM_POOL +#define random_init(e) do { } while (0) +#define random_deinit() do { } while (0) +#define random_add_randomness(b, l) do { } while (0) +#define random_get_bytes(b, l) os_get_random((b), (l)) +#define random_pool_ready() 1 +#define random_mark_pool_ready() do { } while (0) +#else /* CONFIG_NO_RANDOM_POOL */ +void random_init(const char *entropy_file); +void random_deinit(void); +void random_add_randomness(const void *buf, size_t len); +int random_get_bytes(void *buf, size_t len); +int random_pool_ready(void); +void random_mark_pool_ready(void); +#endif /* CONFIG_NO_RANDOM_POOL */ + +#endif /* RANDOM_H */ diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c index b061373..c3a74c3 100644 --- a/src/crypto/sha256-internal.c +++ b/src/crypto/sha256-internal.c @@ -1,6 +1,6 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2011, Jouni Malinen * * 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 @@ -18,10 +18,12 @@ #include "sha256.h" #include "crypto.h" +#define SHA256_BLOCK_SIZE 64 + struct sha256_state { u64 length; u32 state[8], curlen; - u8 buf[64]; + u8 buf[SHA256_BLOCK_SIZE]; }; static void sha256_init(struct sha256_state *md); @@ -162,28 +164,27 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in, unsigned long inlen) { unsigned long n; -#define block_size 64 - if (md->curlen > sizeof(md->buf)) + if (md->curlen >= sizeof(md->buf)) return -1; while (inlen > 0) { - if (md->curlen == 0 && inlen >= block_size) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { if (sha256_compress(md, (unsigned char *) in) < 0) return -1; - md->length += block_size * 8; - in += block_size; - inlen -= block_size; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; } else { - n = MIN(inlen, (block_size - md->curlen)); + n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen)); os_memcpy(md->buf + md->curlen, in, n); md->curlen += n; in += n; inlen -= n; - if (md->curlen == block_size) { + if (md->curlen == SHA256_BLOCK_SIZE) { if (sha256_compress(md, md->buf) < 0) return -1; - md->length += 8 * block_size; + md->length += 8 * SHA256_BLOCK_SIZE; md->curlen = 0; } } @@ -217,14 +218,14 @@ static int sha256_done(struct sha256_state *md, unsigned char *out) * encoding like normal. */ if (md->curlen > 56) { - while (md->curlen < 64) { + while (md->curlen < SHA256_BLOCK_SIZE) { md->buf[md->curlen++] = (unsigned char) 0; } sha256_compress(md, md->buf); md->curlen = 0; } - /* pad upto 56 bytes of zeroes */ + /* pad up to 56 bytes of zeroes */ while (md->curlen < 56) { md->buf[md->curlen++] = (unsigned char) 0; } diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 0928b5b..a5de3fb 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -24,8 +24,6 @@ struct tls_keys { size_t client_random_len; const u8 *server_random; size_t server_random_len; - const u8 *inner_secret; /* TLS/IA inner secret */ - size_t inner_secret_len; }; enum tls_event { @@ -72,6 +70,7 @@ struct tls_config { const char *pkcs11_engine_path; const char *pkcs11_module_path; int fips_mode; + int cert_in_cb; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -114,7 +113,6 @@ struct tls_config { * specific for now) * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine - * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) * @flags: Parameter options (TLS_CONN_*) * * TLS connection parameters to be configured with tls_connection_set_params() @@ -142,7 +140,6 @@ struct tls_connection_params { const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; - int tls_ia; /* OpenSSL specific variables */ int engine; @@ -282,20 +279,6 @@ int __must_check tls_connection_set_verify(void *tls_ctx, int verify_peer); /** - * tls_connection_set_ia - Set TLS/IA parameters - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @tls_ia: 1 = enable TLS/IA - * Returns: 0 on success, -1 on failure - * - * This function is used to configure TLS/IA in server mode where - * tls_connection_set_params() is not used. - */ -int __must_check tls_connection_set_ia(void *tls_ctx, - struct tls_connection *conn, - int tls_ia); - -/** * tls_connection_get_keys - Get master key and random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() @@ -514,7 +497,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn); -#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */ /** * tls_capabilities - Get supported TLS capabilities * @tls_ctx: TLS context data from tls_init() @@ -522,42 +504,6 @@ int tls_connection_get_keyblock_size(void *tls_ctx, */ unsigned int tls_capabilities(void *tls_ctx); -/** - * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished - * Returns: Encrypted TLS/IA data, %NULL on failure - * - * This function is used to send the TLS/IA end phase message, e.g., when the - * EAP server completes EAP-TTLSv1. - */ -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final); - -/** - * tls_connection_ia_final_phase_finished - Has final phase been completed - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1 - * on failure - */ -int __must_check tls_connection_ia_final_phase_finished( - void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @key: Session key material (session_key vectors with 2-octet length), or - * %NULL if no session key was generating in the current phase - * @key_len: Length of session key material - * Returns: 0 on success, -1 on failure - */ -int __must_check tls_connection_ia_permute_inner_secret( - void *tls_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index c3a7358..afa5268 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -19,28 +19,12 @@ #include #endif /* PKCS12_FUNCS */ -#ifdef CONFIG_GNUTLS_EXTRA -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 -#define GNUTLS_IA -#include -#if LIBGNUTLS_VERSION_NUMBER == 0x010302 -/* This function is not included in the current gnutls/extra.h even though it - * should be, so define it here as a workaround for the time being. */ -int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum); -#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */ -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ -#endif /* CONFIG_GNUTLS_EXTRA */ - #include "common.h" #include "tls.h" -#ifndef TLS_RANDOM_SIZE -#define TLS_RANDOM_SIZE 32 -#endif -#ifndef TLS_MASTER_SIZE -#define TLS_MASTER_SIZE 48 -#endif +#define WPA_TLS_RANDOM_SIZE 32 +#define WPA_TLS_MASTER_SIZE 48 #if LIBGNUTLS_VERSION_NUMBER < 0x010302 @@ -77,9 +61,9 @@ typedef struct { gnutls_mac_algorithm_t write_mac_algorithm; gnutls_compression_method_t write_compression_algorithm; cipher_suite_st current_cipher_suite; - opaque master_secret[TLS_MASTER_SIZE]; - opaque client_random[TLS_RANDOM_SIZE]; - opaque server_random[TLS_RANDOM_SIZE]; + opaque master_secret[WPA_TLS_MASTER_SIZE]; + opaque client_random[WPA_TLS_RANDOM_SIZE]; + opaque server_random[WPA_TLS_RANDOM_SIZE]; /* followed by stuff we are not interested in */ } security_parameters_st; @@ -118,21 +102,6 @@ struct tls_connection { int params_set; gnutls_certificate_credentials_t xcred; - - int tls_ia; - int final_phase_finished; - -#ifdef GNUTLS_IA - gnutls_ia_server_credentials_t iacred_srv; - gnutls_ia_client_credentials_t iacred_cli; - - /* Session keys generated in the current phase for inner secret - * permutation before generating/verifying PhaseFinished. */ - u8 *session_keys; - size_t session_keys_len; - - u8 inner_secret[TLS_MASTER_SIZE]; -#endif /* GNUTLS_IA */ }; @@ -285,8 +254,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + const char *err; +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; const int protos[2] = { GNUTLS_TLS1, 0 }; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ int ret; ret = gnutls_init(&conn->session, @@ -301,6 +274,15 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", + &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " + "'%s'", err); + goto fail; + } +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ ret = gnutls_certificate_type_set_priority(conn->session, cert_types); if (ret < 0) goto fail; @@ -308,6 +290,7 @@ static int tls_gnutls_init_session(struct tls_global *global, ret = gnutls_protocol_set_priority(conn->session, protos); if (ret < 0) goto fail; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); @@ -364,17 +347,6 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) if (conn == NULL) return; -#ifdef GNUTLS_IA - if (conn->iacred_srv) - gnutls_ia_free_server_credentials(conn->iacred_srv); - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } -#endif /* GNUTLS_IA */ - gnutls_certificate_free_credentials(conn->xcred); gnutls_deinit(conn->session); os_free(conn->pre_shared_secret); @@ -407,14 +379,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) wpabuf_free(conn->push_buf); conn->push_buf = NULL; conn->established = 0; - conn->final_phase_finished = 0; -#ifdef GNUTLS_IA - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; -#endif /* GNUTLS_IA */ gnutls_deinit(conn->session); if (tls_gnutls_init_session(global, conn)) { @@ -597,11 +561,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -646,7 +612,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } - conn->tls_ia = params->tls_ia; conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, @@ -656,28 +621,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); } -#ifdef GNUTLS_IA - if (conn->iacred_cli) - gnutls_ia_free_client_credentials(conn->iacred_cli); - - ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_cli); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_client_credentials(conn->iacred_cli); - conn->iacred_cli = NULL; - return -1; - } -#endif /* GNUTLS_IE */ - return ret; } @@ -729,11 +672,13 @@ int tls_global_set_params(void *tls_ctx, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -822,10 +767,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, os_memset(keys, 0, sizeof(*keys)); +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK sec = &conn->session->security_parameters; keys->master_key = sec->master_secret; - keys->master_key_len = TLS_MASTER_SIZE; + keys->master_key_len = WPA_TLS_MASTER_SIZE; keys->client_random = sec->client_random; keys->server_random = sec->server_random; #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ @@ -835,16 +781,12 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, (u8 *) gnutls_session_get_server_random(conn->session); /* No access to master_secret */ #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ -#ifdef GNUTLS_IA - gnutls_ia_extract_inner_secret(conn->session, - (char *) conn->inner_secret); - keys->inner_secret = conn->inner_secret; - keys->inner_secret_len = TLS_MASTER_SIZE; -#endif /* GNUTLS_IA */ - - keys->client_random_len = TLS_RANDOM_SIZE; - keys->server_random_len = TLS_RANDOM_SIZE; +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 + keys->client_random_len = WPA_TLS_RANDOM_SIZE; + keys->server_random_len = WPA_TLS_RANDOM_SIZE; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ return 0; } @@ -883,11 +825,13 @@ static int tls_connection_verify_peer(struct tls_connection *conn, if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + *err = GNUTLS_A_INTERNAL_ERROR; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); *err = GNUTLS_A_INSUFFICIENT_SECURITY; } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); @@ -897,6 +841,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_INFO, "TLS: Certificate expired"); *err = GNUTLS_A_CERTIFICATE_EXPIRED; } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ return -1; } @@ -988,7 +933,7 @@ static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) wpabuf_size(ad)); wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); wpabuf_free(ad); @@ -1062,20 +1007,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, goto out; } -#ifdef CONFIG_GNUTLS_EXTRA - if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { - wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); - conn->failed++; - return NULL; - } -#endif /* CONFIG_GNUTLS_EXTRA */ - - if (conn->tls_ia) - wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); - else { - wpa_printf(MSG_DEBUG, "TLS: Handshake completed " - "successfully"); - } + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ @@ -1122,12 +1054,6 @@ struct wpabuf * tls_connection_encrypt(void *tls_ctx, ssize_t res; struct wpabuf *buf; -#ifdef GNUTLS_IA - if (conn->tls_ia) - res = gnutls_ia_send(conn->session, wpabuf_head(in_data), - wpabuf_len(in_data)); - else -#endif /* GNUTLS_IA */ res = gnutls_record_send(conn->session, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { @@ -1170,65 +1096,6 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, if (out == NULL) return NULL; -#ifdef GNUTLS_IA - if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, wpabuf_mhead(out), - wpabuf_size(out)); - if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || - res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { - int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED; - wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished", - __func__, final ? "Final" : "Intermediate"); - - res = gnutls_ia_permute_inner_secret( - conn->session, conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, - conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (res) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute " - "inner secret: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - res = gnutls_ia_verify_endphase(conn->session, - wpabuf_head(out)); - if (res == 0) { - wpa_printf(MSG_DEBUG, "%s: Correct endphase " - "checksum", __func__); - } else { - wpa_printf(MSG_INFO, "%s: Endphase " - "verification failed: %s", - __func__, gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - - if (final) - conn->final_phase_finished = 1; - - return out; - } - - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, (int) res, - gnutls_strerror(res)); - wpabuf_free(out); - return NULL; - } - wpabuf_put(out, res); - return out; - } -#endif /* GNUTLS_IA */ - res = gnutls_record_recv(conn->session, wpabuf_mhead(out), wpabuf_size(out)); if (res < 0) { @@ -1319,133 +1186,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx, unsigned int tls_capabilities(void *tls_ctx) { - unsigned int capa = 0; - -#ifdef GNUTLS_IA - capa |= TLS_CAPABILITY_IA; -#endif /* GNUTLS_IA */ - - return capa; -} - - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ -#ifdef GNUTLS_IA - int ret; - - if (conn == NULL) - return -1; - - conn->tls_ia = tls_ia; - if (!tls_ia) - return 0; - - ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", - gnutls_strerror(ret)); - return -1; - } - - ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, - conn->iacred_srv); - if (ret) { - wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", - gnutls_strerror(ret)); - gnutls_ia_free_server_credentials(conn->iacred_srv); - conn->iacred_srv = NULL; - return -1; - } - - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ -#ifdef GNUTLS_IA - int ret; - struct wpabuf *buf; - - if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return NULL; - - ret = gnutls_ia_permute_inner_secret(conn->session, - conn->session_keys_len, - (char *) conn->session_keys); - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys = NULL; - conn->session_keys_len = 0; - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - ret = gnutls_ia_endphase_send(conn->session, final); - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s", - __func__, gnutls_strerror(ret)); - return NULL; - } - - buf = conn->push_buf; - conn->push_buf = NULL; - return buf; -#else /* GNUTLS_IA */ - return NULL; -#endif /* GNUTLS_IA */ -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - - return conn->final_phase_finished; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ -#ifdef GNUTLS_IA - if (conn == NULL || !conn->tls_ia) - return -1; - - if (conn->session_keys) { - os_memset(conn->session_keys, 0, conn->session_keys_len); - os_free(conn->session_keys); - } - conn->session_keys_len = 0; - - if (key) { - conn->session_keys = os_malloc(key_len); - if (conn->session_keys == NULL) - return -1; - os_memcpy(conn->session_keys, key, key_len); - conn->session_keys_len = key_len; - } else { - conn->session_keys = NULL; - conn->session_keys_len = 0; - } - return 0; -#else /* GNUTLS_IA */ - return -1; -#endif /* GNUTLS_IA */ } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 64124d8..eacb9bf 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -211,6 +211,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + tlsv1_client_set_time_checks( + conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; @@ -287,13 +290,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -608,28 +604,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 0c836bb..927edf5 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -84,13 +84,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -205,25 +198,3 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c index ad834b6..09a1e73 100644 --- a/src/crypto/tls_nss.c +++ b/src/crypto/tls_nss.c @@ -419,13 +419,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys) { @@ -649,28 +642,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index c0a40f9..6380ce0 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -28,6 +28,11 @@ #include #endif /* OPENSSL_NO_ENGINE */ +#ifdef ANDROID +#include +#include "keystore_get.h" +#endif /* ANDROID */ + #include "common.h" #include "crypto.h" #include "tls.h" @@ -54,6 +59,7 @@ struct tls_global { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; + int cert_in_cb; }; static struct tls_global *tls_global = NULL; @@ -81,6 +87,8 @@ struct tls_connection { unsigned int server_cert_only:1; u8 srv_cert_hash[32]; + + unsigned int flags; }; @@ -687,6 +695,7 @@ void * tls_init(const struct tls_config *conf) if (conf) { tls_global->event_cb = conf->event_cb; tls_global->cb_ctx = conf->cb_ctx; + tls_global->cert_in_cb = conf->cert_in_cb; } #ifdef CONFIG_FIPS @@ -711,7 +720,7 @@ void * tls_init(const struct tls_config *conf) #endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); -#ifndef OPENSSL_NO_SHA256 +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded @@ -1137,7 +1146,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe) { + if (conn->cert_probe || tls_global->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1178,13 +1187,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; + if (conn == NULL) + return 0; + match = conn->subject_match; + altmatch = conn->altsubject_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; if (!preverify_ok && depth > 0 && conn->server_cert_only) preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity " + "time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -1290,6 +1308,19 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ +#ifdef ANDROID +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + char value[KEYSTORE_MESSAGE_SIZE]; + int length = keystore_get(key, strlen(key), value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + return bio; +} +#endif /* ANDROID */ + + static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) @@ -1380,6 +1411,36 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return 0; } +#ifdef ANDROID + if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&ca_cert[11]); + STACK_OF(X509_INFO) *stack = NULL; + int i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (!stack) + return -1; + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + if (info->x509) { + X509_STORE_add_cert(ssl_ctx->cert_store, + info->x509); + } + if (info->crl) { + X509_STORE_add_crl(ssl_ctx->cert_store, + info->crl); + } + } + sk_X509_INFO_pop_free(stack, X509_INFO_free); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* ANDROID */ + #ifdef CONFIG_NATIVE_WINDOWS if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == 0) { @@ -1550,26 +1611,42 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL) return -1; +#ifdef ANDROID + if (os_strncmp("keystore://", client_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&client_cert[11]); + X509 *x509 = NULL; + int ret = -1; + if (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (x509) { + if (SSL_use_certificate(conn->ssl, x509) == 1) + ret = 0; + X509_free(x509); + } + return ret; + } +#endif /* ANDROID */ + #ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_ASN1) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (DER) failed"); } if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { + ERR_clear_error(); wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (PEM) failed"); } + + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file failed"); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ @@ -1900,10 +1977,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_RSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" - " failed"); } if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, @@ -1913,10 +1986,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_DSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" - " failed"); } if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, @@ -1926,9 +1995,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_RSAPrivateKey_ASN1 --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_RSAPrivateKey_ASN1 failed"); } if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, @@ -1942,6 +2008,26 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } +#ifdef ANDROID + if (!ok && private_key && + os_strncmp("keystore://", private_key, 11) == 0) { + BIO *bio = BIO_from_keystore(&private_key[11]); + EVP_PKEY *pkey = NULL; + if (bio) { + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (pkey) { + if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Private key " + "from keystore"); + ok = 1; + } + EVP_PKEY_free(pkey); + } + } +#endif /* ANDROID */ + while (!ok && private_key) { #ifndef OPENSSL_NO_STDIO if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -1950,10 +2036,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (DER) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (DER) " - "failed"); } if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -1962,10 +2044,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (PEM) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (PEM) " - "failed"); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1991,9 +2069,9 @@ static int tls_connection_private_key(void *_ssl_ctx, } if (!ok) { - wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); os_free(passwd); - ERR_clear_error(); return -1; } ERR_clear_error(); @@ -2663,6 +2741,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + conn->flags = params->flags; + tls_get_errors(tls_ctx); return 0; @@ -2731,35 +2811,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c index 4a94e99..a33d24e 100644 --- a/src/crypto/tls_schannel.c +++ b/src/crypto/tls_schannel.c @@ -736,32 +736,3 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } - - -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final); -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} diff --git a/src/drivers/driver.h b/src/drivers/driver.h index fa49da4..c7b7363 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -31,6 +31,9 @@ #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 #define HOSTAPD_CHAN_NO_IBSS 0x00000004 #define HOSTAPD_CHAN_RADAR 0x00000008 +#define HOSTAPD_CHAN_HT40PLUS 0x00000010 +#define HOSTAPD_CHAN_HT40MINUS 0x00000020 +#define HOSTAPD_CHAN_HT40 0x00000040 /** * struct hostapd_channel_data - Channel information @@ -57,6 +60,8 @@ struct hostapd_channel_data { u8 max_tx_power; }; +#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) + /** * struct hostapd_hw_modes - Supported hardware mode information */ @@ -100,6 +105,8 @@ struct hostapd_hw_modes { * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters */ u8 a_mpdu_params; + + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ }; @@ -192,7 +199,7 @@ struct wpa_interface_info { const char *drv_name; }; -#define WPAS_MAX_SCAN_SSIDS 4 +#define WPAS_MAX_SCAN_SSIDS 16 /** * struct wpa_driver_scan_params - Scan parameters @@ -261,6 +268,15 @@ struct wpa_driver_scan_params { * num_filter_ssids - Number of entries in filter_ssids array */ size_t num_filter_ssids; + + /** + * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes + * + * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 + * Mbps from the support rates element(s) in the Probe Request frames + * and not to transmit the frames at any of those rates. + */ + u8 p2p_probe; }; /** @@ -279,6 +295,19 @@ struct wpa_driver_auth_params { size_t wep_key_len[4]; int wep_tx_keyidx; int local_state_change; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + +}; + +enum wps_mode { + WPS_MODE_NONE /* no WPS provisioning being used */, + WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */, + WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection + */ }; /** @@ -335,6 +364,11 @@ struct wpa_driver_associate_params { size_t wpa_ie_len; /** + * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 + */ + unsigned int wpa_proto; + + /** * pairwise_suite - Selected pairwise cipher suite * * This is usually ignored if @wpa_ie is used. @@ -460,6 +494,184 @@ struct wpa_driver_associate_params { * association. */ const u8 *prev_bssid; + + /** + * wps - WPS mode + * + * If the driver needs to do special configuration for WPS association, + * this variable provides more information on what type of association + * is being requested. Most drivers should not need ot use this. + */ + enum wps_mode wps; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + /** + * uapsd - UAPSD parameters for the network + * -1 = do not change defaults + * AP mode: 1 = enabled, 0 = disabled + * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE + */ + int uapsd; +}; + +enum hide_ssid { + NO_SSID_HIDING, + HIDDEN_SSID_ZERO_LEN, + HIDDEN_SSID_ZERO_CONTENTS +}; + +struct wpa_driver_ap_params { + /** + * head - Beacon head from IEEE 802.11 header to IEs before TIM IE + */ + const u8 *head; + + /** + * head_len - Length of the head buffer in octets + */ + size_t head_len; + + /** + * tail - Beacon tail following TIM IE + */ + const u8 *tail; + + /** + * tail_len - Length of the tail buffer in octets + */ + size_t tail_len; + + /** + * dtim_period - DTIM period + */ + int dtim_period; + + /** + * beacon_int - Beacon interval + */ + int beacon_int; + + /** + * ssid - The SSID to use in Beacon/Probe Response frames + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * hide_ssid - Whether to hide the SSID + */ + enum hide_ssid hide_ssid; + + /** + * pairwise_ciphers - WPA_CIPHER_* bitfield + */ + unsigned int pairwise_ciphers; + + /** + * group_cipher - WPA_CIPHER_* + */ + unsigned int group_cipher; + + /** + * key_mgmt_suites - WPA_KEY_MGMT_* bitfield + */ + unsigned int key_mgmt_suites; + + /** + * auth_algs - WPA_AUTH_ALG_* bitfield + */ + unsigned int auth_algs; + + /** + * wpa_version - WPA_PROTO_* bitfield + */ + unsigned int wpa_version; + + /** + * privacy - Whether privacy is used in the BSS + */ + int privacy; + + /** + * beacon_ies - WPS/P2P IE(s) for Beacon frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that do + * not use the full Beacon template. + */ + const struct wpabuf *beacon_ies; + + /** + * proberesp_ies - P2P/WPS IE(s) for Probe Response frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that + * reply to Probe Request frames internally. + */ + const struct wpabuf *proberesp_ies; + + /** + * assocresp_ies - WPS IE(s) for (Re)Association Response frames + * + * This is used to add IEs like WPS IE by drivers that reply to + * (Re)Association Request frames internally. + */ + const struct wpabuf *assocresp_ies; + + /** + * isolate - Whether to isolate frames between associated stations + * + * If this is non-zero, the AP is requested to disable forwarding of + * frames between association stations. + */ + int isolate; + + /** + * cts_protect - Whether CTS protection is enabled + */ + int cts_protect; + + /** + * preamble - Whether short preamble is enabled + */ + int preamble; + + /** + * short_slot_time - Whether short slot time is enabled + * + * 0 = short slot time disable, 1 = short slot time enabled, -1 = do + * not set (e.g., when 802.11g mode is not in use) + */ + int short_slot_time; + + /** + * ht_opmode - HT operation mode or -1 if HT not in use + */ + int ht_opmode; + + /** + * interworking - Whether Interworking is enabled + */ + int interworking; + + /** + * hessid - Homogeneous ESS identifier or %NULL if not set + */ + const u8 *hessid; + + /** + * access_network_type - Access Network Type (0..15) + * + * This is used for filtering Probe Request frames when Interworking is + * enabled. + */ + u8 access_network_type; }; /** @@ -490,7 +702,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 /* Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 -#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004 +/* unused: 0x00000004 */ /* Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 @@ -502,14 +714,60 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_AP 0x00000040 /* Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 +/* Driver takes care of P2P management operations */ +#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100 +/* Driver supports concurrent P2P operations */ +#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 +/* + * Driver uses the initial interface as a dedicated management interface, i.e., + * it cannot be used for P2P group operations or non-P2P purposes. + */ +#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 +/* This interface is P2P capable (P2P Device, GO, or P2P Client */ +#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 +/* Driver supports concurrent operations on multiple channels */ +#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000 +/* + * Driver uses the initial interface for P2P management interface and non-P2P + * purposes (e.g., connect to infra AP), but this interface cannot be used for + * P2P group operations. + */ +#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 +/* + * Driver is known to use sane error codes, i.e., when it indicates that + * something (e.g., association) fails, there was indeed a failure and the + * operation does not end up getting completed successfully later. + */ +#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 +/* Driver supports off-channel TX */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 +/* Driver indicates TX status events for EAPOL Data frames */ +#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 +/* Driver indicates TX status events for Deauth/Disassoc frames */ +#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 +/* Driver supports roaming (BSS selection) in firmware */ +#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 +/* Driver supports operating as a TDLS peer */ +#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 +/* Driver requires external TDLS setup/teardown/discovery */ +#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 unsigned int flags; int max_scan_ssids; + int max_sched_scan_ssids; + int sched_scan_supported; + int max_match_sets; /** * max_remain_on_chan - Maximum remain-on-channel duration in msec */ unsigned int max_remain_on_chan; + + /** + * max_stations - Maximum number of associated stations the driver + * supports in AP mode + */ + unsigned int max_stations; }; @@ -535,6 +793,8 @@ struct hostapd_sta_add_params { size_t supp_rates_len; u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; + u32 flags; /* bitmask of WPA_STA_* flags */ + int set; /* Set STA parameters instead of add */ }; struct hostapd_freq_params { @@ -567,9 +827,26 @@ enum wpa_driver_if_type { * This interface has its own address and Beacon frame. */ WPA_IF_AP_BSS, + + /** + * WPA_IF_P2P_GO - P2P Group Owner + */ + WPA_IF_P2P_GO, + + /** + * WPA_IF_P2P_CLIENT - P2P Client + */ + WPA_IF_P2P_CLIENT, + + /** + * WPA_IF_P2P_GROUP - P2P Group interface (will become either + * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) + */ + WPA_IF_P2P_GROUP }; struct wpa_init_params { + void *global_priv; const u8 *bssid; const char *ifname; const u8 *ssid; @@ -595,12 +872,46 @@ struct wpa_bss_params { int wpa_pairwise; int wpa_key_mgmt; int rsn_preauth; + enum mfp_options ieee80211w; }; #define WPA_STA_AUTHORIZED BIT(0) #define WPA_STA_WMM BIT(1) #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) +#define WPA_STA_TDLS_PEER BIT(4) + +/** + * struct p2p_params - P2P parameters for driver-based P2P management + */ +struct p2p_params { + const char *dev_name; + u8 pri_dev_type[8]; +#define DRV_MAX_SEC_DEV_TYPES 5 + u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8]; + size_t num_sec_dev_types; +}; + +enum tdls_oper { + TDLS_DISCOVERY_REQ, + TDLS_SETUP, + TDLS_TEARDOWN, + TDLS_ENABLE_LINK, + TDLS_DISABLE_LINK, + TDLS_ENABLE, + TDLS_DISABLE +}; + +/** + * struct wpa_signal_info - Information about channel signal quality + */ +struct wpa_signal_info { + u32 frequency; + int above_threshold; + int current_signal; + int current_noise; + int current_txrate; +}; /** * struct wpa_driver_ops - Driver interface API definition @@ -652,8 +963,12 @@ struct wpa_driver_ops { * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); * %WPA_ALG_NONE clears the key. - * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for - * broadcast/default keys + * @addr: Address of the peer STA (BSSID of the current AP when setting + * pairwise key in station mode), ff:ff:ff:ff:ff:ff for + * broadcast keys, %NULL for default keys that are used both for + * broadcast and unicast; when clearing keys, %NULL is used to + * indicate that both the broadcast-only and default key of the + * specified key index is to be cleared * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for * IGTK * @set_tx: configure this key as the default Tx key (only used when @@ -661,7 +976,7 @@ struct wpa_driver_ops { * @seq: sequence number/packet number, seq_len octets, the next * packet number to be used for in replay protection; configured * for Rx keys (in most cases, this is only used with broadcast - * keys and set to zero for unicast keys) + * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, @@ -684,7 +999,7 @@ struct wpa_driver_ops { * Please note that TKIP keys include separate TX and RX MIC keys and * some drivers may expect them in different order than wpa_supplicant * is using. If the TX/RX keys are swapped, all TKIP encrypted packets - * will tricker Michael MIC errors. This can be fixed by changing the + * will trigger Michael MIC errors. This can be fixed by changing the * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. @@ -951,93 +1266,21 @@ struct wpa_driver_ops { * flags: Variable for returning hardware feature flags * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. - * - * This function is only needed for drivers that export MLME - * (management frame processing) to %wpa_supplicant or hostapd. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, u16 *flags); /** - * set_channel - Set channel - * @priv: Private driver interface data - * @phymode: HOSTAPD_MODE_IEEE80211B, .. - * @chan: IEEE 802.11 channel number - * @freq: Frequency of the channel in MHz - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan, - int freq); - - /** - * set_ssid - Set SSID - * @priv: Private driver interface data - * @ssid: SSID - * @ssid_len: SSID length - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len); - - /** - * set_bssid - Set BSSID - * @priv: Private driver interface data - * @bssid: BSSID - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*set_bssid)(void *priv, const u8 *bssid); - - /** * send_mlme - Send management frame from MLME * @priv: Private driver interface data * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len); /** - * mlme_add_sta - Add a STA entry into the driver/netstack - * @priv: Private driver interface data - * @addr: MAC address of the STA (e.g., BSSID of the AP) - * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11 - * format (one octet per rate, 1 = 0.5 Mbps) - * @supp_rates_len: Number of entries in supp_rates - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. When the MLME code - * completes association with an AP, this function is called to - * configure the driver/netstack with a STA entry for data frame - * processing (TX rate control, encryption/decryption). - */ - int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates, - size_t supp_rates_len); - - /** - * mlme_remove_sta - Remove a STA entry from the driver/netstack - * @priv: Private driver interface data - * @addr: MAC address of the STA (e.g., BSSID of the AP) - * Returns: 0 on success, -1 on failure - * - * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. - */ - int (*mlme_remove_sta)(void *priv, const u8 *addr); - - /** * update_ft_ies - Update FT (IEEE 802.11r) IEs * @priv: Private driver interface data * @md: Mobility domain (2 octets) (also included inside ies) @@ -1164,24 +1407,25 @@ struct wpa_driver_ops { struct wpa_driver_auth_params *params); /** - * set_beacon - Set Beacon frame template + * set_ap - Set Beacon and Probe Response information for AP mode * @priv: Private driver interface data - * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE - * @head_len: Length of the head buffer in octets - * @tail: Beacon tail following TIM IE - * @tail_len: Length of the tail buffer in octets - * @dtim_period: DTIM period - * @beacon_int: Beacon interval - * Returns: 0 on success, -1 on failure + * @params: Parameters to use in AP mode * - * This function is used to configure Beacon template for the driver in + * This function is used to configure Beacon template and/or extra IEs + * to add for Beacon and Probe Response frames for the driver in * AP mode. The driver is responsible for building the full Beacon * frame by concatenating the head part with TIM IE generated by the - * driver/firmware and finishing with the tail part. + * driver/firmware and finishing with the tail part. Depending on the + * driver architectue, this can be done either by using the full + * template or the set of additional IEs (e.g., WPS and P2P IE). + * Similarly, Probe Response processing depends on the driver design. + * If the driver (or firmware) takes care of replying to Probe Request + * frames, the extra IEs provided here needs to be added to the Probe + * Response frames. + * + * Returns: 0 on success, -1 on failure */ - int (*set_beacon)(void *priv, const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, int dtim_period, - int beacon_int); + int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** * hapd_init - Initialize driver interface (hostapd only) @@ -1190,7 +1434,7 @@ struct wpa_driver_ops { * Returns: Pointer to private data, %NULL on failure * * This function is used instead of init() or init2() when the driver - * wrapper is used withh hostapd. + * wrapper is used with hostapd. */ void * (*hapd_init)(struct hostapd_data *hapd, struct wpa_init_params *params); @@ -1210,8 +1454,10 @@ struct wpa_driver_ops { * This is an optional function to configure the kernel driver to * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This * can be left undefined (set to %NULL) if IEEE 802.1X support is - * always enabled and the driver uses set_beacon() to set WPA/RSN IE + * always enabled and the driver uses set_ap() to set WPA/RSN IE * for Beacon frames. + * + * DEPRECATED - use set_ap() instead */ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); @@ -1223,7 +1469,9 @@ struct wpa_driver_ops { * * This is an optional function to configure privacy field in the * kernel driver for Beacon frames. This can be left undefined (set to - * %NULL) if the driver uses the Beacon template from set_beacon(). + * %NULL) if the driver uses the Beacon template from set_ap(). + * + * DEPRECATED - use set_ap() instead */ int (*set_privacy)(void *priv, int enabled); @@ -1265,7 +1513,9 @@ struct wpa_driver_ops { * This is an optional function to add information elements in the * kernel driver for Beacon and Probe Response frames. This can be left * undefined (set to %NULL) if the driver uses the Beacon template from - * set_beacon(). + * set_ap(). + * + * DEPRECATED - use set_ap() instead */ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); @@ -1287,12 +1537,13 @@ struct wpa_driver_ops { * @data_len: Length of the EAPOL packet in octets * @encrypt: Whether the frame should be encrypted * @own_addr: Source MAC address + * @flags: WPA_STA_* flags for the destination station * * Returns: 0 on success, -1 on failure */ int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr); + const u8 *own_addr, u32 flags); /** * sta_deauth - Deauthenticate a station (AP only) @@ -1338,8 +1589,7 @@ struct wpa_driver_ops { * Returns: Length of the SSID on success, -1 on failure * * This function need not be implemented if the driver uses Beacon - * template from set_beacon() and does not reply to Probe Request - * frames. + * template from set_ap() and does not reply to Probe Request frames. */ int (*hapd_get_ssid)(void *priv, u8 *buf, int len); @@ -1349,6 +1599,8 @@ struct wpa_driver_ops { * @buf: SSID * @len: Length of the SSID in octets * Returns: 0 on success, -1 on failure + * + * DEPRECATED - use set_ap() instead */ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); @@ -1372,6 +1624,9 @@ struct wpa_driver_ops { * This function is used to add a station entry to the driver once the * station has completed association. This is only used if the driver * does not take care of association processing. + * + * With TDLS, this function is also used to add or set (params->set 1) + * TDLS peer entries. */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); @@ -1439,33 +1694,9 @@ struct wpa_driver_ops { int mode); /** - * set_cts_protect - Set CTS protection mode (AP only) - * @priv: Private driver interface data - * @value: Whether CTS protection is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_cts_protect)(void *priv, int value); - - /** - * set_preamble - Set preamble mode (AP only) - * @priv: Private driver interface data - * @value: Whether short preamble is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_preamble)(void *priv, int value); - - /** - * set_short_slot_time - Set short slot time (AP only) - * @priv: Private driver interface data - * @value: Whether short slot time is enabled - * Returns: 0 on success, -1 on failure - */ - int (*set_short_slot_time)(void *priv, int value); - - /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data - * @queue: Queue number + * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) * @aifs: AIFS * @cw_min: cwMin * @cw_max: cwMax @@ -1475,17 +1706,6 @@ struct wpa_driver_ops { int cw_max, int burst_time); /** - * valid_bss_mask - Validate BSSID mask - * @priv: Private driver interface data - * @addr: Address - * @mask: Mask - * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can - * be used, but the main interface address must be the first address in - * the block if mask is applied - */ - int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); - - /** * if_add - Add a virtual interface * @priv: Private driver interface data * @type: Interface type @@ -1500,11 +1720,13 @@ struct wpa_driver_ops { * @if_addr: Buffer for returning the allocated interface address * (this may differ from the requested addr if the driver cannot * change interface address) + * @bridge: Bridge interface to use or %NULL if no bridge configured * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, - void **drv_priv, char *force_ifname, u8 *if_addr); + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge); /** * if_remove - Remove a virtual interface @@ -1578,33 +1800,36 @@ struct wpa_driver_ops { int (*set_radius_acl_expire)(void *priv, const u8 *mac); /** - * set_ht_params - Set HT parameters (AP only) - * @priv: Private driver interface data - * @ht_capab: HT Capabilities IE - * @ht_capab_len: Length of ht_capab in octets - * @ht_oper: HT Operation IE - * @ht_oper_len: Length of ht_oper in octets - * Returns: 0 on success, -1 on failure - */ - int (*set_ht_params)(void *priv, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len); - - /** * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) * @priv: Private driver interface data * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove * extra IE(s) + * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL + * to remove extra IE(s) * Returns: 0 on success, -1 on failure * * This is an optional function to add WPS IE in the kernel driver for * Beacon and Probe Response frames. This can be left undefined (set - * to %NULL) if the driver uses the Beacon template from set_beacon() - * and does not process Probe Request frames. + * to %NULL) if the driver uses the Beacon template from set_ap() + * and does not process Probe Request frames. If the driver takes care + * of (Re)Association frame processing, the assocresp buffer includes + * WPS IE(s) that need to be added to (Re)Association Response frames + * whenever a (Re)Association Request frame indicated use of WPS. + * + * This will also be used to add P2P IE(s) into Beacon/Probe Response + * frames when operating as a GO. The driver is responsible for adding + * timing related attributes (e.g., NoA) in addition to the IEs + * included here by appending them after these buffers. This call is + * also used to provide Probe Response IEs for P2P Listen state + * operations for drivers that generate the Probe Response frames + * internally. + * + * DEPRECATED - use set_ap() instead */ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp); + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); /** * set_supp_port - Set IEEE 802.1X Supplicant Port status @@ -1620,31 +1845,52 @@ struct wpa_driver_ops { * @addr: MAC address of the associated station * @aid: Association ID * @val: 1 = bind to 4-address WDS; 0 = unbind + * @bridge_ifname: Bridge interface to use for the WDS station or %NULL + * to indicate that bridge is not to be used * Returns: 0 on success, -1 on failure */ - int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val); + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname); /** * send_action - Transmit an Action frame * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel + * @wait: Time to wait off-channel for a response (in ms), or zero * @dst: Destination MAC address (Address 1) * @src: Source MAC address (Address 2) * @bssid: BSSID (Address 3) * @data: Frame body * @data_len: data length in octets + @ @no_cck: Whether CCK rates must not be used to transmit this frame * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action - * frame to the specified destination. If a remain-on-channel duration - * is in progress, the frame is transmitted on that channel. Otherwise, - * the frame is transmitted on the current operational channel if in - * associated state in station mode or if operating as an AP. If none - * of these conditions is in effect, send_action() cannot be used. + * frame to the specified destination. + * + * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will + * be transmitted on the given channel and the device will wait for a + * response on that channel for the given wait time. + * + * If the flag is not set, the wait time will be ignored. In this case, + * if a remain-on-channel duration is in progress, the frame must be + * transmitted on that channel; alternatively the frame may be sent on + * the current operational channel (if in associated state in station + * mode or while operating as an AP.) */ - int (*send_action)(void *priv, unsigned int freq, + int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len); + const u8 *data, size_t data_len, int no_cck); + + /** + * send_action_cancel_wait - Cancel action frame TX wait + * @priv: Private driver interface data + * + * This command cancels the wait time associated with sending an action + * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is + * set in the driver flags. + */ + void (*send_action_cancel_wait)(void *priv); /** * remain_on_channel - Remain awake on a channel @@ -1701,28 +1947,25 @@ struct wpa_driver_ops { int (*probe_req_report)(void *priv, int report); /** - * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX + * deinit_ap - Deinitialize AP mode * @priv: Private driver interface data - * @disabled: Whether IEEE 802.11b rates are disabled * Returns: 0 on success, -1 on failure (or if not supported) * - * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and - * 11 Mbps) as TX rates for data and management frames. This can be - * used to optimize channel use when there is no need to support IEEE - * 802.11b-only devices. + * This optional function can be used to disable AP mode related + * configuration and change the driver mode to station mode to allow + * normal station operations like scanning to be completed. */ - int (*disable_11b_rates)(void *priv, int disabled); + int (*deinit_ap)(void *priv); /** - * deinit_ap - Deinitialize AP mode + * deinit_p2p_cli - Deinitialize P2P client mode * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * - * This optional function can be used to disable AP mode related - * configuration and change the driver mode to station mode to allow - * normal station operations like scanning to be completed. + * This optional function can be used to disable P2P client mode. It + * can be used to change the interface type back to station mode. */ - int (*deinit_ap)(void *priv); + int (*deinit_p2p_cli)(void *priv); /** * suspend - Notification on system suspend/hibernate event @@ -1765,46 +2008,500 @@ struct wpa_driver_ops { */ int (*send_frame)(void *priv, const u8 *data, size_t data_len, int encrypt); -}; - -/** - * enum wpa_event_type - Event type for wpa_supplicant_event() calls - */ -enum wpa_event_type { /** - * EVENT_ASSOC - Association completed + * shared_freq - Get operating frequency of shared interface(s) + * @priv: Private driver interface data + * Returns: Operating frequency in MHz, 0 if no shared operation in + * use, or -1 on failure * - * This event needs to be delivered when the driver completes IEEE - * 802.11 association or reassociation successfully. - * wpa_driver_ops::get_bssid() is expected to provide the current BSSID - * after this event has been generated. In addition, optional - * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide - * more information about the association. If the driver interface gets - * both of these events at the same time, it can also include the - * assoc_info data in EVENT_ASSOC call. + * This command can be used to request the current operating frequency + * of any virtual interface that shares the same radio to provide + * information for channel selection for other virtual interfaces. */ - EVENT_ASSOC, + int (*shared_freq)(void *priv); /** - * EVENT_DISASSOC - Association lost + * get_noa - Get current Notice of Absence attribute payload + * @priv: Private driver interface data + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure * - * This event should be called when association is lost either due to - * receiving deauthenticate or disassociate frame from the AP or when - * sending either of these frames to the current AP. If the driver - * supports separate deauthentication event, EVENT_DISASSOC should only - * be used for disassociation and EVENT_DEAUTH for deauthentication. - * In AP mode, union wpa_event_data::disassoc_info is required. + * This function is used to fetch the current Notice of Absence + * attribute value from GO. */ - EVENT_DISASSOC, + int (*get_noa)(void *priv, u8 *buf, size_t buf_len); /** - * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected + * set_noa - Set Notice of Absence parameters for GO (testing) + * @priv: Private driver interface data + * @count: Count + * @start: Start time in ms from next TBTT + * @duration: Duration in ms + * Returns: 0 on success or -1 on failure * - * This event must be delivered when a Michael MIC error is detected by - * the local driver. Additional data for event processing is - * provided with union wpa_event_data::michael_mic_failure. This - * information is used to request new encyption key and to initiate + * This function is used to set Notice of Absence parameters for GO. It + * is used only for testing. To disable NoA, all parameters are set to + * 0. + */ + int (*set_noa)(void *priv, u8 count, int start, int duration); + + /** + * set_p2p_powersave - Set P2P power save options + * @priv: Private driver interface data + * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change + * @opp_ps: 0 = disable, 1 = enable, -1 = no change + * @ctwindow: 0.. = change (msec), -1 = no change + * Returns: 0 on success or -1 on failure + */ + int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, + int ctwindow); + + /** + * ampdu - Enable/disable aggregation + * @priv: Private driver interface data + * @ampdu: 1/0 = enable/disable A-MPDU aggregation + * Returns: 0 on success or -1 on failure + */ + int (*ampdu)(void *priv, int ampdu); + + /** + * get_radio_name - Get physical radio name for the device + * @priv: Private driver interface data + * Returns: Radio name or %NULL if not known + * + * The returned data must not be modified by the caller. It is assumed + * that any interface that has the same radio name as another is + * sharing the same physical radio. This information can be used to + * share scan results etc. information between the virtual interfaces + * to speed up various operations. + */ + const char * (*get_radio_name)(void *priv); + + /** + * p2p_find - Start P2P Device Discovery + * @priv: Private driver interface data + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type (enum p2p_discovery_type) + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_find)(void *priv, unsigned int timeout, int type); + + /** + * p2p_stop_find - Stop P2P Device Discovery + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_stop_find)(void *priv); + + /** + * p2p_listen - Start P2P Listen state for specified duration + * @priv: Private driver interface data + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the + * device discoverable on the listen channel for an extended set of + * time. At least in its current form, this is mainly used for testing + * purposes and may not be of much use for normal P2P operations. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_listen)(void *priv, unsigned int timeout); + + /** + * p2p_connect - Start P2P group formation (GO negotiation) + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @wps_method: enum p2p_wps_method value indicating config method + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the + * group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create persistent group + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group); + + /** + * wps_success_cb - Report successfully completed WPS provisioning + * @priv: Private driver interface data + * @peer_addr: Peer address + * Returns: 0 on success, -1 on failure + * + * This function is used to report successfully completed WPS + * provisioning during group formation in both GO/Registrar and + * client/Enrollee roles. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*wps_success_cb)(void *priv, const u8 *peer_addr); + + /** + * p2p_group_formation_failed - Report failed WPS provisioning + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function is used to report failed group formation. This can + * happen either due to failed WPS provisioning or due to 15 second + * timeout during the provisioning phase. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_group_formation_failed)(void *priv); + + /** + * p2p_set_params - Set P2P parameters + * @priv: Private driver interface data + * @params: P2P parameters + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_set_params)(void *priv, const struct p2p_params *params); + + /** + * p2p_prov_disc_req - Send Provision Discovery Request + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to + * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared + * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The + * Provision Discovery Request frame is transmitted once immediately + * and if no response is received, the frame will be sent again + * whenever the target device is discovered during device dsicovery + * (start with a p2p_find() call). Response from the peer is indicated + * with the EVENT_P2P_PROV_DISC_RESPONSE event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr, + u16 config_methods, int join); + + /** + * p2p_sd_request - Schedule a service discovery query + * @priv: Private driver interface data + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or 0 on failure + * + * Response to the query is indicated with the + * EVENT_P2P_SD_RESPONSE driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + u64 (*p2p_sd_request)(void *priv, const u8 *dst, + const struct wpabuf *tlvs); + + /** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @priv: Private driver interface data + * @req: Query reference from p2p_sd_request() + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_cancel_request)(void *priv, u64 req); + + /** + * p2p_sd_response - Send response to a service discovery query + * @priv: Private driver interface data + * @freq: Frequency from EVENT_P2P_SD_REQUEST event + * @dst: Destination address from EVENT_P2P_SD_REQUEST event + * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event + * @resp_tlvs: P2P Service Response TLV(s) + * Returns: 0 on success, -1 on failure + * + * This function is called as a response to the request indicated with + * the EVENT_P2P_SD_REQUEST driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, + u8 dialog_token, + const struct wpabuf *resp_tlvs); + + /** + * p2p_service_update - Indicate a change in local services + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function needs to be called whenever there is a change in + * availability of the local services. This will increment the + * Service Update Indicator value which will be used in SD Request and + * Response frames. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_service_update)(void *priv); + + /** + * p2p_reject - Reject peer device (explicitly block connections) + * @priv: Private driver interface data + * @addr: MAC address of the peer + * Returns: 0 on success, -1 on failure + */ + int (*p2p_reject)(void *priv, const u8 *addr); + + /** + * p2p_invite - Invite a P2P Device into a group + * @priv: Private driver interface data + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * Returns: 0 on success, -1 on failure + */ + int (*p2p_invite)(void *priv, const u8 *peer, int role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, int persistent_group); + + /** + * send_tdls_mgmt - for sending TDLS management packets + * @priv: private driver interface data + * @dst: Destination (peer) MAC address + * @action_code: TDLS action code for the mssage + * @dialog_token: Dialog Token to use in the message (if needed) + * @status_code: Status Code or Reason Code to use (if needed) + * @buf: TDLS IEs to add to the message + * @len: Length of buf in octets + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send packet to driver which is + * responsible for receiving and sending all TDLS packets. + */ + int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len); + + /** + * tdls_oper - Ask the driver to perform high-level TDLS operations + * @priv: Private driver interface data + * @oper: TDLS high-level operation. See %enum tdls_oper + * @peer: Destination (peer) MAC address + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send high-level TDLS commands + * to the driver. + */ + int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); + + /** + * signal_poll - Get current connection information + * @priv: Private driver interface data + * @signal_info: Connection info structure + */ + int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); + + /** + * set_authmode - Set authentication algorithm(s) for static WEP + * @priv: Private driver interface data + * @authmode: 1=Open System, 2=Shared Key, 3=both + * Returns: 0 on success, -1 on failure + * + * This function can be used to set authentication algorithms for AP + * mode when static WEP is used. If the driver uses user space MLME/SME + * implementation, there is no need to implement this function. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_authmode)(void *priv, int authmode); + + /** + * set_rekey_info - Set rekey information + * @priv: Private driver interface data + * @kek: Current KEK + * @kck: Current KCK + * @replay_ctr: Current EAPOL-Key Replay Counter + * + * This optional function can be used to provide information for the + * driver/firmware to process EAPOL-Key frames in Group Key Handshake + * while the host (including wpa_supplicant) is sleeping. + */ + void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); + + /** + * sta_assoc - Station association indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for association frame + * @addr: MAC address of the station to associate + * @reassoc: flag to indicate re-association + * @status: association response status code + * @ie: assoc response ie buffer + * @len: ie buffer length + * Returns: 0 on success, -1 on failure + * + * This function indicates the driver to send (Re)Association + * Response frame to the station. + */ + int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); + + /** + * sta_auth - Station authentication indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for authentication frame + * @addr: MAC address of the station to associate + * @seq: authentication sequence number + * @status: authentication response status code + * @ie: authentication frame ie buffer + * @len: ie buffer length + * + * This function indicates the driver to send Authentication frame + * to the station. + */ + int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); + + /** + * add_tspec - Add traffic stream + * @priv: Private driver interface data + * @addr: MAC address of the station to associate + * @tspec_ie: tspec ie buffer + * @tspec_ielen: tspec ie length + * Returns: 0 on success, -1 on failure + * + * This function adds the traffic steam for the station + * and fills the medium_time in tspec_ie. + */ + int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen); + + /** + * add_sta_node - Add a station node in the driver + * @priv: Private driver interface data + * @addr: MAC address of the station to add + * @auth_alg: authentication algorithm used by the station + * Returns: 0 on success, -1 on failure + * + * This function adds the station node in the driver, when + * the station gets added by FT-over-DS. + */ + int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); + + /** + * sched_scan - Request the driver to initiate scheduled scan + * @priv: Private driver interface data + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure + * + * This operation should be used for scheduled scan offload to + * the hardware. Every time scan results are available, the + * driver should report scan results event for wpa_supplicant + * which will eventually request the results with + * wpa_driver_get_scan_results2(). This operation is optional + * and if not provided or if it returns -1, we fall back to + * normal host-scheduled scans. + */ + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, + u32 interval); + + /** + * stop_sched_scan - Request the driver to stop a scheduled scan + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This should cause the scheduled scan to be stopped and + * results should stop being sent. Must be supported if + * sched_scan is supported. + */ + int (*stop_sched_scan)(void *priv); + + /** + * poll_client - Probe (null data or such) the given station + * @priv: Private driver interface data + * @own_addr: MAC address of sending interface + * @addr: MAC address of the station to probe + * @qos: Indicates whether station is QoS station + * + * This function is used to verify whether an associated station is + * still present. This function does not need to be implemented if the + * driver provides such inactivity polling mechanism. + */ + void (*poll_client)(void *priv, const u8 *own_addr, + const u8 *addr, int qos); +}; + + +/** + * enum wpa_event_type - Event type for wpa_supplicant_event() calls + */ +enum wpa_event_type { + /** + * EVENT_ASSOC - Association completed + * + * This event needs to be delivered when the driver completes IEEE + * 802.11 association or reassociation successfully. + * wpa_driver_ops::get_bssid() is expected to provide the current BSSID + * after this event has been generated. In addition, optional + * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide + * more information about the association. If the driver interface gets + * both of these events at the same time, it can also include the + * assoc_info data in EVENT_ASSOC call. + */ + EVENT_ASSOC, + + /** + * EVENT_DISASSOC - Association lost + * + * This event should be called when association is lost either due to + * receiving deauthenticate or disassociate frame from the AP or when + * sending either of these frames to the current AP. If the driver + * supports separate deauthentication event, EVENT_DISASSOC should only + * be used for disassociation and EVENT_DEAUTH for deauthentication. + * In AP mode, union wpa_event_data::disassoc_info is required. + */ + EVENT_DISASSOC, + + /** + * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected + * + * This event must be delivered when a Michael MIC error is detected by + * the local driver. Additional data for event processing is + * provided with union wpa_event_data::michael_mic_failure. This + * information is used to request new encyption key and to initiate * TKIP countermeasures if needed. */ EVENT_MICHAEL_MIC_FAILURE, @@ -1887,6 +2584,13 @@ enum wpa_event_type { EVENT_STKSTART, /** + * EVENT_TDLS - Request TDLS operation + * + * This event can be used to request a TDLS operation to be performed. + */ + EVENT_TDLS, + + /** * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs * * The driver is expected to report the received FT IEs from @@ -1930,7 +2634,7 @@ enum wpa_event_type { * EVENT_ASSOC_REJECT - Association rejected * * This event should be called when (re)association attempt has been - * rejected by the AP. Information about authentication result is + * rejected by the AP. Information about the association response is * included in union wpa_event_data::assoc_reject. */ EVENT_ASSOC_REJECT, @@ -2046,7 +2750,144 @@ enum wpa_event_type { * observed in frames received from the current AP if signal strength * monitoring has been enabled with signal_monitor(). */ - EVENT_SIGNAL_CHANGE + EVENT_SIGNAL_CHANGE, + + /** + * EVENT_INTERFACE_ENABLED - Notify that interface was enabled + * + * This event is used to indicate that the interface was enabled after + * having been previously disabled, e.g., due to rfkill. + */ + EVENT_INTERFACE_ENABLED, + + /** + * EVENT_INTERFACE_DISABLED - Notify that interface was disabled + * + * This event is used to indicate that the interface was disabled, + * e.g., due to rfkill. + */ + EVENT_INTERFACE_DISABLED, + + /** + * EVENT_CHANNEL_LIST_CHANGED - Channel list changed + * + * This event is used to indicate that the channel list has changed, + * e.g., because of a regulatory domain change triggered by scan + * results including an AP advertising a country code. + */ + EVENT_CHANNEL_LIST_CHANGED, + + /** + * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable + * + * This event is used to indicate that the driver cannot maintain this + * interface in its operation mode anymore. The most likely use for + * this is to indicate that AP mode operation is not available due to + * operating channel would need to be changed to a DFS channel when + * the driver does not support radar detection and another virtual + * interfaces caused the operating channel to change. Other similar + * resource conflicts could also trigger this for station mode + * interfaces. + */ + EVENT_INTERFACE_UNAVAILABLE, + + /** + * EVENT_BEST_CHANNEL + * + * Driver generates this event whenever it detects a better channel + * (e.g., based on RSSI or channel use). This information can be used + * to improve channel selection for a new AP/P2P group. + */ + EVENT_BEST_CHANNEL, + + /** + * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received + * + * This event should be called when a Deauthentication frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_deauth is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DEAUTH, + + /** + * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received + * + * This event should be called when a Disassociation frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_disassoc is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DISASSOC, + + /** + * EVENT_STATION_LOW_ACK + * + * Driver generates this event whenever it detected that a particular + * station was lost. Detection can be through massive transmission + * failures for example. + */ + EVENT_STATION_LOW_ACK, + + /** + * EVENT_P2P_DEV_FOUND - Report a discovered P2P device + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_dev_found. + */ + EVENT_P2P_DEV_FOUND, + + /** + * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_req_rx. + */ + EVENT_P2P_GO_NEG_REQ_RX, + + /** + * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_completed. + */ + EVENT_P2P_GO_NEG_COMPLETED, + + EVENT_P2P_PROV_DISC_REQUEST, + EVENT_P2P_PROV_DISC_RESPONSE, + EVENT_P2P_SD_REQUEST, + EVENT_P2P_SD_RESPONSE, + + /** + * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore + */ + EVENT_IBSS_PEER_LOST, + + /** + * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey + * + * This event carries the new replay counter to notify wpa_supplicant + * of the current EAPOL-Key Replay Counter in case the driver/firmware + * completed Group Key Handshake while the host (including + * wpa_supplicant was sleeping). + */ + EVENT_DRIVER_GTK_REKEY, + + /** + * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped + */ + EVENT_SCHED_SCAN_STOPPED, + + /** + * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll + * + * This event indicates that the station responded to the poll + * initiated with @poll_client. + */ + EVENT_DRIVER_CLIENT_POLL_OK }; @@ -2064,6 +2905,11 @@ union wpa_event_data { */ struct assoc_info { /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + + /** * req_ies - (Re)Association Request IEs * * If the driver generates WPA/RSN IE, this event data must be @@ -2146,6 +2992,16 @@ union wpa_event_data { * Deauthentication frame */ u16 reason_code; + + /** + * ie - Optional IE(s) in Disassociation frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; } disassoc_info; /** @@ -2162,6 +3018,16 @@ union wpa_event_data { * Deauthentication frame */ u16 reason_code; + + /** + * ie - Optional IE(s) in Deauthentication frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; } deauth_info; /** @@ -2202,6 +3068,18 @@ union wpa_event_data { } stkstart; /** + * struct tdls - Data for EVENT_TDLS + */ + struct tdls { + u8 peer[ETH_ALEN]; + enum { + TDLS_REQUEST_SETUP, + TDLS_REQUEST_TEARDOWN + } oper; + u16 reason_code; /* for teardown */ + } tdls; + + /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is @@ -2233,7 +3111,9 @@ union wpa_event_data { */ struct auth_info { u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; u16 auth_type; + u16 auth_transaction; u16 status_code; const u8 *ies; size_t ies_len; @@ -2244,6 +3124,11 @@ union wpa_event_data { */ struct assoc_reject { /** + * bssid - BSSID of the AP that rejected association + */ + const u8 *bssid; + + /** * resp_ies - (Re)Association Response IEs * * Optional association data from the driver. This data is not @@ -2254,7 +3139,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *resp_ies; + const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes @@ -2296,8 +3181,9 @@ union wpa_event_data { * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events */ struct rx_from_unknown { - const u8 *frame; - size_t len; + const u8 *bssid; + const u8 *addr; + int wds; } rx_from_unknown; /** @@ -2405,6 +3291,18 @@ union wpa_event_data { const u8 *sa; /** + * da - Destination address of the received Probe Request frame + * or %NULL if not available + */ + const u8 *da; + + /** + * bssid - BSSID of the received Probe Request frame or %NULL + * if not available + */ + const u8 *bssid; + + /** * ie - IEs from the Probe Request body */ const u8 *ie; @@ -2432,11 +3330,126 @@ union wpa_event_data { } eapol_rx; /** - * struct signal_change - Data for EVENT_SIGNAL_CHANGE events + * signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct wpa_signal_info signal_change; + + /** + * struct best_channel - Data for EVENT_BEST_CHANNEL events + * @freq_24: Best 2.4 GHz band channel frequency in MHz + * @freq_5: Best 5 GHz band channel frequency in MHz + * @freq_overall: Best channel frequency in MHz + * + * 0 can be used to indicate no preference in either band. + */ + struct best_channel { + int freq_24; + int freq_5; + int freq_overall; + } best_chan; + + struct unprot_deauth { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_deauth; + + struct unprot_disassoc { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_disassoc; + + /** + * struct low_ack - Data for EVENT_STATION_LOW_ACK events + * @addr: station address */ - struct signal_change { - int above_threshold; - } signal_change; + struct low_ack { + u8 addr[ETH_ALEN]; + } low_ack; + + /** + * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND + */ + struct p2p_dev_found { + const u8 *addr; + const u8 *dev_addr; + const u8 *pri_dev_type; + const char *dev_name; + u16 config_methods; + u8 dev_capab; + u8 group_capab; + } p2p_dev_found; + + /** + * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX + */ + struct p2p_go_neg_req_rx { + const u8 *src; + u16 dev_passwd_id; + } p2p_go_neg_req_rx; + + /** + * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED + */ + struct p2p_go_neg_completed { + struct p2p_go_neg_results *res; + } p2p_go_neg_completed; + + struct p2p_prov_disc_req { + const u8 *peer; + u16 config_methods; + const u8 *dev_addr; + const u8 *pri_dev_type; + const char *dev_name; + u16 supp_config_methods; + u8 dev_capab; + u8 group_capab; + } p2p_prov_disc_req; + + struct p2p_prov_disc_resp { + const u8 *peer; + u16 config_methods; + } p2p_prov_disc_resp; + + struct p2p_sd_req { + int freq; + const u8 *sa; + u8 dialog_token; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_req; + + struct p2p_sd_resp { + const u8 *sa; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_resp; + + /** + * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST + */ + struct ibss_peer_lost { + u8 peer[ETH_ALEN]; + } ibss_peer_lost; + + /** + * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY + */ + struct driver_gtk_rekey { + const u8 *bssid; + const u8 *replay_ctr; + } driver_gtk_rekey; + + /** + * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events + * @addr: station address + */ + struct client_poll { + u8 addr[ETH_ALEN]; + } client_poll; }; /** @@ -2459,10 +3472,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, */ static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, - size_t ielen) + size_t ielen, int reassoc) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); + event.assoc_info.reassoc = reassoc; event.assoc_info.req_ies = ie; event.assoc_info.req_ies_len = ielen; event.assoc_info.addr = addr; @@ -2488,4 +3502,10 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); } +/* driver_common.c */ +void wpa_scan_results_free(struct wpa_scan_results *res); + +/* Convert wpa_event_type to a string for logging */ +const char * event_to_string(enum wpa_event_type event); + #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 5c25f00..788171d 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -34,7 +34,7 @@ */ #define ATH_WPS_IE -#include "os/linux/include/ieee80211_external.h" +#include "ieee80211_external.h" #ifdef CONFIG_WPS @@ -56,7 +56,7 @@ #include "linux_ioctl.h" -struct madwifi_driver_data { +struct atheros_driver_data { struct hostapd_data *hapd; /* back pointer */ char iface[IFNAMSIZ + 1]; @@ -70,11 +70,14 @@ struct madwifi_driver_data { struct hostap_sta_driver_data acct_data; struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ + struct wpabuf *wpa_ie; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; }; -static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, +static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code); -static int madwifi_set_privacy(void *priv, int enabled); +static int atheros_set_privacy(void *priv, int enabled); static const char * athr_get_ioctl_name(int op) { @@ -125,16 +128,8 @@ static const char * athr_get_ioctl_name(int op) return "FILTERFRAME"; case IEEE80211_IOCTL_SET_RTPARAMS: return "SET_RTPARAMS"; - case IEEE80211_IOCTL_SENDADDBA: - return "SENDADDBA"; - case IEEE80211_IOCTL_GETADDBASTATUS: - return "GETADDBASTATUS"; - case IEEE80211_IOCTL_SENDDELBA: - return "SENDDELBA"; case IEEE80211_IOCTL_SET_MEDENYENTRY: return "SET_MEDENYENTRY"; - case IEEE80211_IOCTL_SET_ADDBARESP: - return "SET_ADDBARESP"; case IEEE80211_IOCTL_GET_MACADDR: return "GET_MACADDR"; case IEEE80211_IOCTL_SET_HBRPARAMS: @@ -179,7 +174,7 @@ static const char * athr_get_param_name(int op) static int -set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) { struct iwreq iwr; int do_inline = len < IFNAMSIZ; @@ -218,7 +213,7 @@ set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) } static int -set80211param(struct madwifi_driver_data *drv, int op, int arg) +set80211param(struct atheros_driver_data *drv, int op, int arg) { struct iwreq iwr; @@ -255,7 +250,7 @@ ether_sprintf(const u8 *addr) * Configure WPA parameters. */ static int -madwifi_configure_wpa(struct madwifi_driver_data *drv, +atheros_configure_wpa(struct atheros_driver_data *drv, struct wpa_bss_params *params) { int v; @@ -320,6 +315,14 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv, v = 0; if (params->rsn_preauth) v |= BIT(0); +#ifdef CONFIG_IEEE80211W + if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + v |= BIT(7); + if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + v |= BIT(6); + } +#endif /* CONFIG_IEEE80211W */ + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, params->rsn_preauth); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { @@ -336,9 +339,9 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv, } static int -madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); @@ -348,14 +351,14 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) IEEE80211_AUTH_AUTO) < 0) return -1; /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ - return madwifi_set_privacy(drv, 0); + return atheros_set_privacy(drv, 0); } if (!params->wpa && !params->ieee802_1x) { hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); return -1; } - if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + if (params->wpa && atheros_configure_wpa(drv, params) != 0) { hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); return -1; @@ -371,9 +374,9 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) } static int -madwifi_set_privacy(void *priv, int enabled) +atheros_set_privacy(void *priv, int enabled) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); @@ -381,9 +384,9 @@ madwifi_set_privacy(void *priv, int enabled) } static int -madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -406,21 +409,21 @@ madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) } static int -madwifi_sta_set_flags(void *priv, const u8 *addr, +atheros_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, int flags_and) { /* For now, only support setting Authorized flag */ if (flags_or & WPA_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); + return atheros_set_sta_authorized(priv, addr, 1); if (!(flags_and & WPA_STA_AUTHORIZED)) - return madwifi_set_sta_authorized(priv, addr, 0); + return atheros_set_sta_authorized(priv, addr, 0); return 0; } static int -madwifi_del_key(void *priv, const u8 *addr, int key_idx) +atheros_del_key(void *priv, const u8 *addr, int key_idx) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_del_key wk; int ret; @@ -446,17 +449,17 @@ madwifi_del_key(void *priv, const u8 *addr, int key_idx) } static int -madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, +atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; u_int8_t cipher; int ret; if (alg == WPA_ALG_NONE) - return madwifi_del_key(drv, addr, key_idx); + return atheros_del_key(drv, addr, key_idx); wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", __func__, alg, ether_sprintf(addr), key_idx); @@ -471,6 +474,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, case WPA_ALG_CCMP: cipher = IEEE80211_CIPHER_AES_CCM; break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + cipher = IEEE80211_CIPHER_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ default: printf("%s: unknown/unsupported algorithm %d\n", __func__, alg); @@ -486,10 +494,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; - wk.ik_flags |= IEEE80211_KEY_DEFAULT; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; } else { memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = IEEE80211_KEYIX_NONE; @@ -510,10 +519,10 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, static int -madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, +atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_key wk; wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", @@ -557,20 +566,20 @@ madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, static int -madwifi_flush(void *priv) +atheros_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; memset(allsta, 0xff, IEEE80211_ADDR_LEN); - return madwifi_sta_deauth(priv, NULL, allsta, + return atheros_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } static int -madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, +atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, const u8 *addr) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_sta_stats stats; memset(data, 0, sizeof(*data)); @@ -602,9 +611,9 @@ madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, static int -madwifi_sta_clear_stats(void *priv, const u8 *addr) +atheros_sta_clear_stats(void *priv, const u8 *addr) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -624,20 +633,61 @@ madwifi_sta_clear_stats(void *priv, const u8 *addr) static int -madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) { - /* - * Do nothing; we setup parameters at startup that define the - * contents of the beacon information element. - */ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *app_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) ie_len); + wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); + + wpabuf_free(drv->wpa_ie); + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + + app_ie = (struct ieee80211req_getset_appiebuf *) buf; + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + app_ie->app_buflen = ie_len; + + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; + + /* append WPS IE for Beacon */ + if (drv->wps_beacon_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_beacon_ie), + wpabuf_len(drv->wps_beacon_ie)); + app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + + /* append WPS IE for Probe Response */ + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; + if (drv->wps_probe_resp_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_probe_resp_ie), + wpabuf_len(drv->wps_probe_resp_ie)); + app_ie->app_buflen = ie_len + + wpabuf_len(drv->wps_probe_resp_ie); + } else + app_ie->app_buflen = ie_len; + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); return 0; } static int -madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, +atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -658,10 +708,10 @@ madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, } static int -madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, +atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; @@ -682,10 +732,10 @@ madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, } #ifdef CONFIG_WPS -static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { - struct madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; u16 fc; union wpa_event_data event; @@ -703,6 +753,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); @@ -710,7 +762,7 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, } #endif /* CONFIG_WPS */ -static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +static int atheros_receive_probe_req(struct atheros_driver_data *drv) { int ret = 0; #ifdef CONFIG_WPS @@ -725,7 +777,7 @@ static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) return ret; drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, - madwifi_raw_receive, drv, 1); + atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; #endif /* CONFIG_WPS */ @@ -734,43 +786,74 @@ static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) #ifdef CONFIG_WPS static int -madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) { - struct madwifi_driver_data *drv = priv; - u8 buf[256]; + struct atheros_driver_data *drv = priv; + u8 buf[512]; struct ieee80211req_getset_appiebuf *beac_ie; - wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, - (unsigned long) len); + wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, + (unsigned long) len, frametype); + wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; - memcpy(&(beac_ie->app_buf[0]), ie, len); + os_memcpy(&(beac_ie->app_buf[0]), ie, len); + + /* append the WPA/RSN IE if it is set already */ + if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || + (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && + (drv->wpa_ie != NULL)) { + wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", + drv->wpa_ie); + os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), + wpabuf_len(drv->wpa_ie)); + beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", + beac_ie->app_buf, beac_ie->app_buflen); return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, - sizeof(struct ieee80211req_getset_appiebuf) + len); + sizeof(struct ieee80211req_getset_appiebuf) + + beac_ie->app_buflen); } static int -madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) +atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { - if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + struct atheros_driver_data *drv = priv; + + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", + proberesp); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", + assocresp); + wpabuf_free(drv->wps_beacon_ie); + drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; + wpabuf_free(drv->wps_probe_resp_ie); + drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; + + atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, + assocresp ? wpabuf_len(assocresp) : 0, + IEEE80211_APPIE_FRAME_ASSOC_RESP); + if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, IEEE80211_APPIE_FRAME_BEACON)) return -1; - return madwifi_set_wps_ie(priv, + return atheros_set_wps_ie(priv, proberesp ? wpabuf_head(proberesp) : NULL, proberesp ? wpabuf_len(proberesp): 0, IEEE80211_APPIE_FRAME_PROBE_RESP); } #else /* CONFIG_WPS */ -#define madwifi_set_ap_wps_ie NULL +#define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ static void -madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { struct hostapd_data *hapd = drv->hapd; struct ieee80211req_wpaie ie; @@ -791,17 +874,21 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) __func__, strerror(errno)); goto no_ie; } - wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE", ie.wpa_ie, IEEE80211_MAX_OPT_IE); - wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", ie.rsn_ie, IEEE80211_MAX_OPT_IE); +#ifdef ATH_WPS_IE + wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", + ie.wps_ie, IEEE80211_MAX_OPT_IE); +#endif /* ATH_WPS_IE */ iebuf = ie.wpa_ie; - /* madwifi seems to return some random data if WPA/RSN IE is not set. + /* atheros seems to return some random data if WPA/RSN IE is not set. * Assume the IE was not included if the IE type is unknown. */ if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) iebuf[1] = 0; if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { - /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not * set. This is needed for WPA2. */ iebuf = ie.rsn_ie; if (iebuf[0] != WLAN_EID_RSN) @@ -809,13 +896,23 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) } ielen = iebuf[1]; + +#ifdef ATH_WPS_IE + /* if WPS IE is present, preference is given to WPS */ + if (ie.wps_ie && + (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + iebuf = ie.wps_ie; + ielen = ie.wps_ie[1]; + } +#endif /* ATH_WPS_IE */ + if (ielen == 0) iebuf = NULL; else ielen += 2; no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen); + drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ @@ -825,7 +922,7 @@ no_ie: } static void -madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, +atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, char *custom, char *end) { wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); @@ -897,14 +994,14 @@ madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, "length %d", len); return; } - madwifi_raw_receive(drv, NULL, + atheros_raw_receive(drv, NULL, (u8 *) custom + WPS_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS */ } } static void -madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, +atheros_wireless_event_wireless(struct atheros_driver_data *drv, char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; @@ -943,7 +1040,7 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVREGISTERED: - madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data); break; case IWEVASSOCREQIE: /* Driver hack.. Use IWEVASSOCREQIE to bypass @@ -958,7 +1055,7 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; - madwifi_wireless_event_wireless_custom( + atheros_wireless_event_wireless_custom( drv, buf, buf + iwe->u.data.length); free(buf); break; @@ -970,10 +1067,10 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, static void -madwifi_wireless_event_rtm_newlink(void *ctx, +atheros_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; @@ -986,7 +1083,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx, rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { - madwifi_wireless_event_wireless( + atheros_wireless_event_wireless( drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } @@ -996,7 +1093,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx, static int -madwifi_get_we_version(struct madwifi_driver_data *drv) +atheros_get_we_version(struct atheros_driver_data *drv) { struct iw_range *range; struct iwreq iwr; @@ -1036,23 +1133,23 @@ madwifi_get_we_version(struct madwifi_driver_data *drv) drv->we_version = range->we_version_compiled; } - free(range); + os_free(range); return 0; } static int -madwifi_wireless_event_init(struct madwifi_driver_data *drv) +atheros_wireless_event_init(struct atheros_driver_data *drv) { struct netlink_config *cfg; - madwifi_get_we_version(drv); + atheros_get_we_version(drv); cfg = os_zalloc(sizeof(*cfg)); if (cfg == NULL) return -1; cfg->ctx = drv; - cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + cfg->newlink_cb = atheros_wireless_event_rtm_newlink; drv->netlink = netlink_init(cfg); if (drv->netlink == NULL) { os_free(cfg); @@ -1064,10 +1161,10 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv) static int -madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) +atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; unsigned char buf[3000]; unsigned char *bp = buf; struct l2_ethhdr *eth; @@ -1107,22 +1204,22 @@ madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, static void handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { - struct madwifi_driver_data *drv = ctx; + struct atheros_driver_data *drv = ctx; drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), len - sizeof(struct l2_ethhdr)); } static void * -madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) { - struct madwifi_driver_data *drv; + struct atheros_driver_data *drv; struct ifreq ifr; struct iwreq iwr; char brname[IFNAMSIZ]; - drv = os_zalloc(sizeof(struct madwifi_driver_data)); + drv = os_zalloc(sizeof(struct atheros_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for madwifi driver data\n"); + printf("Could not allocate memory for atheros driver data\n"); return NULL; } @@ -1179,11 +1276,11 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) /* mark down during setup */ linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - madwifi_set_privacy(drv, 0); /* default to no privacy */ + atheros_set_privacy(drv, 0); /* default to no privacy */ - madwifi_receive_probe_req(drv); + atheros_receive_probe_req(drv); - if (madwifi_wireless_event_init(drv)) + if (atheros_wireless_event_init(drv)) goto bad; return drv; @@ -1201,9 +1298,9 @@ bad: static void -madwifi_deinit(void *priv) +atheros_deinit(void *priv) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); @@ -1215,13 +1312,16 @@ madwifi_deinit(void *priv) l2_packet_deinit(drv->sock_xmit); if (drv->sock_raw) l2_packet_deinit(drv->sock_raw); + wpabuf_free(drv->wpa_ie); + wpabuf_free(drv->wps_beacon_ie); + wpabuf_free(drv->wps_probe_resp_ie); free(drv); } static int -madwifi_set_ssid(void *priv, const u8 *buf, int len) +atheros_set_ssid(void *priv, const u8 *buf, int len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); @@ -1239,9 +1339,9 @@ madwifi_set_ssid(void *priv, const u8 *buf, int len) } static int -madwifi_get_ssid(void *priv, u8 *buf, int len) +atheros_get_ssid(void *priv, u8 *buf, int len) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; struct iwreq iwr; int ret = 0; @@ -1249,6 +1349,8 @@ madwifi_get_ssid(void *priv, u8 *buf, int len) os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = len; + iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? + IW_ESSID_MAX_SIZE : len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { perror("ioctl[SIOCGIWESSID]"); @@ -1260,39 +1362,86 @@ madwifi_get_ssid(void *priv, u8 *buf, int len) } static int -madwifi_set_countermeasures(void *priv, int enabled) +atheros_set_countermeasures(void *priv, int enabled) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); } static int -madwifi_commit(void *priv) +atheros_commit(void *priv) { - struct madwifi_driver_data *drv = priv; + struct atheros_driver_data *drv = priv; return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } +static int atheros_set_authmode(void *priv, int auth_algs) +{ + int authmode; + + if ((auth_algs & WPA_AUTH_ALG_OPEN) && + (auth_algs & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_algs & WPA_AUTH_ALG_OPEN) + authmode = IEEE80211_AUTH_OPEN; + else if (auth_algs & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + return -1; + + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); +} + +static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) +{ + /* + * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, + * set_generic_elem, and hapd_set_ssid. + */ + + wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " + "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " + "wpa_version=0x%x privacy=%d interworking=%d", + params->pairwise_ciphers, params->group_cipher, + params->key_mgmt_suites, params->auth_algs, + params->wpa_version, params->privacy, params->interworking); + wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", + params->ssid, params->ssid_len); + if (params->hessid) + wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, + MAC2STR(params->hessid)); + wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", + params->beacon_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", + params->proberesp_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", + params->assocresp_ies); + + return 0; +} + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", - .hapd_init = madwifi_init, - .hapd_deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .set_key = madwifi_set_key, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .hapd_send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .hapd_set_ssid = madwifi_set_ssid, - .hapd_get_ssid = madwifi_get_ssid, - .set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_ap_wps_ie = madwifi_set_ap_wps_ie, + .hapd_init = atheros_init, + .hapd_deinit = atheros_deinit, + .set_ieee8021x = atheros_set_ieee8021x, + .set_privacy = atheros_set_privacy, + .set_key = atheros_set_key, + .get_seqnum = atheros_get_seqnum, + .flush = atheros_flush, + .set_generic_elem = atheros_set_opt_ie, + .sta_set_flags = atheros_sta_set_flags, + .read_sta_data = atheros_read_sta_driver_data, + .hapd_send_eapol = atheros_send_eapol, + .sta_disassoc = atheros_sta_disassoc, + .sta_deauth = atheros_sta_deauth, + .hapd_set_ssid = atheros_set_ssid, + .hapd_get_ssid = atheros_get_ssid, + .set_countermeasures = atheros_set_countermeasures, + .sta_clear_stats = atheros_sta_clear_stats, + .commit = atheros_commit, + .set_ap_wps_ie = atheros_set_ap_wps_ie, + .set_authmode = atheros_set_authmode, + .set_ap = atheros_set_ap, }; diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c deleted file mode 100644 index cbec6c3..0000000 --- a/src/drivers/driver_atmel.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers - * Copyright (c) 2000-2005, ATMEL Corporation - * Copyright (c) 2004-2007, Jouni Malinen - * - * 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. - */ - -/****************************************************************************** - Copyright 2000-2001 ATMEL Corporation. - - WPA Supplicant - driver interaction with Atmel Wireless lan drivers. - - This is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -******************************************************************************/ - -/* - * Alternatively, this software may be distributed under the terms of BSD - * license. - */ - -#include "includes.h" -#include - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_atmel_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - - -#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2) -#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3) -#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4) - - -/* ATMEL_WPA_IOCTL ioctl() cmd: */ -enum { - SET_WPA_ENCRYPTION = 1, - SET_CIPHER_SUITES = 2, - MLME_STA_DEAUTH = 3, - MLME_STA_DISASSOC = 4 -}; - -/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */ -enum { - ATMEL_PARAM_WPA = 1, - ATMEL_PARAM_PRIVACY_INVOKED = 2, - ATMEL_PARAM_WPA_TYPE = 3 -}; - -#define MAX_KEY_LENGTH 40 - -struct atmel_param{ - unsigned char sta_addr[6]; - int cmd; - u8 alg; - u8 key_idx; - u8 set_tx; - u8 seq[8]; - u8 seq_len; - u16 key_len; - u8 key[MAX_KEY_LENGTH]; - struct{ - int reason_code; - u8 state; - }mlme; - u8 pairwise_suite; - u8 group_suite; - u8 key_mgmt_suite; -}; - - - -static int atmel_ioctl(struct wpa_driver_atmel_data *drv, - struct atmel_param *param, - int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) { - int ret; - ret = errno; - if (show_err) - perror("ioctl[ATMEL_WPA_IOCTL]"); - return ret; - } - - return 0; -} - - -static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value) -{ - struct iwreq iwr; - int *i, ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; - - if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) { - perror("ioctl[ATMEL_WPA_IOCTL_PARAM]"); - ret = -1; - } - return ret; -} - - -#if 0 -static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv, - const char *wpa_ie, size_t wpa_ie_len) -{ - struct atmel_param *param; - int res; - size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = ATMEL_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = wpa_ie_len; - os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); - res = atmel_ioctl(drv, param, blen, 1); - - os_free(param); - - return res; -} -#endif - - -static int wpa_driver_atmel_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; - - printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname); - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - -#if 0 - if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; -#endif - if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0) - ret = -1; - if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0) - ret = -1; - - return ret; -} - - -static int wpa_driver_atmel_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; - struct atmel_param *param; - u8 *buf; - u8 alg_type; - - size_t blen; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - alg_type = 0; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - alg_type = 1; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - alg_type = 2; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - alg_type = 3; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct atmel_param *) buf; - - param->cmd = SET_WPA_ENCRYPTION; - - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); - - param->alg = alg_type; - param->key_idx = key_idx; - param->set_tx = set_tx; - os_memcpy(param->seq, seq, seq_len); - param->seq_len = seq_len; - param->key_len = key_len; - os_memcpy((u8 *)param->key, key, key_len); - - if (atmel_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - /* TODO: show key error*/ - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_atmel_set_countermeasures(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_atmel_set_countermeasures - not yet " - "implemented\n"); - return 0; -} - - -static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - struct atmel_param param; - int ret; - int mgmt_error = 0xaa; - - os_memset(¶m, 0, sizeof(param)); - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.cmd = cmd; - param.mlme.reason_code = reason_code; - param.mlme.state = mgmt_error; - ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); - return ret; -} - - -#if 0 -static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv, - u8 pairwise_suite, u8 group_suite, - u8 key_mgmt_suite) -{ - struct atmel_param param; - int ret; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = SET_CIPHER_SUITES; - param.pairwise_suite = pairwise_suite; - param.group_suite = group_suite; - param.key_mgmt_suite = key_mgmt_suite; - - ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); - return ret; -} -#endif - - -static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - printf("wpa_driver_atmel_deauthenticate\n"); - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH, - reason_code); - -} - - -static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_atmel_data *drv = priv; - printf("wpa_driver_atmel_disassociate\n"); - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC, - reason_code); - -} - - -#if 0 -/* Atmel driver uses specific values for each cipher suite */ -static int convertSuiteToDriver(enum wpa_cipher suite) -{ - u8 suite_type; - - switch(suite) { - case CIPHER_NONE: - suite_type = 0; - break; - case CIPHER_WEP40: - suite_type = 1; - break; - case CIPHER_TKIP: - suite_type = 2; - break; - case CIPHER_WEP104: - suite_type = 5; - break; - case CIPHER_CCMP: - suite_type = 3; - break; - default: - suite_type = 2; - } - - return suite_type; - -} -#endif - -static int -wpa_driver_atmel_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_atmel_data *drv = priv; - int ret = 0; -#if 0 - u8 pairwise_suite_driver; - u8 group_suite_driver; - u8 key_mgmt_suite_driver; - - pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite); - group_suite_driver = convertSuiteToDriver(params->group_suite); - key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite); - - if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver, - group_suite_driver, - key_mgmt_suite_driver) < 0){ - printf("wpa_driver_atmel_set_suites.\n"); - ret = -1; - } - if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) { - printf("wpa_driver_atmel_set_freq.\n"); - ret = -1; - } -#endif - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) - < 0) { - printf("FAILED : wpa_driver_atmel_set_ssid.\n"); - ret = -1; - } - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) { - printf("FAILED : wpa_driver_atmel_set_bssid.\n"); - ret = -1; - } - - return ret; -} - - -static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_atmel_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_atmel_set_operstate(void *priv, int state) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_atmel_init(void *ctx, const char *ifname) -{ - struct wpa_driver_atmel_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_driver_atmel_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_driver_atmel_deinit(void *priv) -{ - struct wpa_driver_atmel_data *drv = priv; - wpa_driver_atmel_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_atmel_ops = { - .name = "atmel", - .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", - .get_bssid = wpa_driver_atmel_get_bssid, - .get_ssid = wpa_driver_atmel_get_ssid, - .set_key = wpa_driver_atmel_set_key, - .init = wpa_driver_atmel_init, - .deinit = wpa_driver_atmel_deinit, - .set_countermeasures = wpa_driver_atmel_set_countermeasures, - .scan2 = wpa_driver_atmel_scan, - .get_scan_results2 = wpa_driver_atmel_get_scan_results, - .deauthenticate = wpa_driver_atmel_deauthenticate, - .disassociate = wpa_driver_atmel_disassociate, - .associate = wpa_driver_atmel_associate, - .set_operstate = wpa_driver_atmel_set_operstate, -}; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 99de6c7..4596a51 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -15,11 +15,13 @@ #include "includes.h" #include +#include #include "common.h" #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" #include #include @@ -295,9 +297,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, if (alg == WPA_ALG_NONE) { #ifndef HOSTAPD - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - IEEE80211_ADDR_LEN) == 0) + if (addr == NULL || is_broadcast_ether_addr(addr)) return bsd_del_key(priv, NULL, key_idx); else #endif /* HOSTAPD */ @@ -334,8 +334,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, * the address (yech). Note also that we can only mark global * keys default; doing this for a unicast key is an error. */ - if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - IEEE80211_ADDR_LEN) == 0) { + if (is_broadcast_ether_addr(addr)) { wk.ik_flags |= IEEE80211_KEY_GROUP; wk.ik_keyix = key_idx; } else { @@ -346,7 +345,20 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; wk.ik_keylen = key_len; - os_memcpy(&wk.ik_keyrsc, seq, seq_len); + if (seq) { +#ifdef WORDS_BIGENDIAN + /* + * wk.ik_keyrsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 *keyrsc = (u8 *) &wk.ik_keyrsc; + for (i = 0; i < seq_len; i++) + keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i]; +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + } os_memcpy(wk.ik_keydata, key, key_len); return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); @@ -511,12 +523,12 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(ctx, addr, iebuf, ielen); + drv_event_assoc(ctx, addr, iebuf, ielen, 0); } static int bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) + int encrypt, const u8 *own_addr, u32 flags) { struct bsd_driver_data *drv = priv; @@ -527,20 +539,30 @@ bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, } static int -bsd_set_freq(void *priv, u16 channel) +bsd_set_freq(void *priv, struct hostapd_freq_params *freq) { struct bsd_driver_data *drv = priv; #ifdef SIOCS80211CHANNEL struct ieee80211chanreq creq; #endif /* SIOCS80211CHANNEL */ u32 mode; - - if (channel < 14) - mode = IFM_IEEE80211_11G; - else if (channel == 14) + int channel = freq->channel; + + if (channel < 14) { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NG : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11G; + } else if (channel == 14) { mode = IFM_IEEE80211_11B; - else - mode = IFM_IEEE80211_11A; + } else { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NA : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11A; + } if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set modulation mode", __func__); @@ -550,7 +572,7 @@ bsd_set_freq(void *priv, u16 channel) #ifdef SIOCS80211CHANNEL os_memset(&creq, 0, sizeof(creq)); os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); - creq.i_channel = channel; + creq.i_channel = (u_int16_t)channel; return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); #else /* SIOCS80211CHANNEL */ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); @@ -569,6 +591,21 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) return 0; } +static int +rtbuf_len(void) +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__, + strerror(errno)); + len = 2048; + } + + return len; +} #ifdef HOSTAPD @@ -691,26 +728,37 @@ static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = ctx; - char buf[2048]; + char *buf; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; struct ieee80211_join_event *join; struct ieee80211_leave_event *leave; - int n; + int n, len; union wpa_event_data data; - n = read(sock, buf, sizeof(buf)); + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } + + n = read(sock, buf, len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - perror("read(PF_ROUTE)"); + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); return; } rtm = (struct rt_msghdr *) buf; if (rtm->rtm_version != RTM_VERSION) { - wpa_printf(MSG_DEBUG, "Routing message version %d not " - "understood\n", rtm->rtm_version); + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); return; } ifan = (struct if_announcemsghdr *) rtm; @@ -751,6 +799,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) } break; } + os_free(buf); } static void @@ -760,12 +809,6 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) drv_event_eapol_rx(drv->hapd, src_addr, buf, len); } -static int -hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - return bsd_set_freq(priv, freq->channel); -} - static void * bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) { @@ -972,7 +1015,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) struct bsd_driver_data *drv = priv; struct ieee80211req_mlme mlme; u32 mode; - u16 channel; int privacy; int ret = 0; @@ -1007,18 +1049,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) } if (params->mode == IEEE80211_MODE_AP) { - if (params->freq >= 2412 && params->freq <= 2472) - channel = (params->freq - 2407) / 5; - else if (params->freq == 2484) - channel = 14; - else if ((params->freq >= 5180 && params->freq <= 5240) || - (params->freq >= 5745 && params->freq <= 5825)) - channel = (params->freq - 5000) / 5; - else - channel = 0; - if (bsd_set_freq(drv, channel) < 0) - return -1; - drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, handle_read, drv, 0); if (drv->sock_xmit == NULL) @@ -1134,7 +1164,7 @@ static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = sock_ctx; - char buf[2048]; + char *buf; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1142,19 +1172,30 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) struct ieee80211_michael_event *mic; struct ieee80211_leave_event *leave; struct ieee80211_join_event *join; - int n; + int n, len; + + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } - n = read(sock, buf, sizeof(buf)); + n = read(sock, buf, len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - perror("read(PF_ROUTE)"); + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); return; } rtm = (struct rt_msghdr *) buf; if (rtm->rtm_version != RTM_VERSION) { - wpa_printf(MSG_DEBUG, "Routing message version %d not " - "understood\n", rtm->rtm_version); + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); return; } os_memset(&event, 0, sizeof(event)); @@ -1169,6 +1210,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; default: + os_free(buf); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", @@ -1241,6 +1283,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) } break; } + os_free(buf); } static void @@ -1518,7 +1561,6 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .read_sta_data = bsd_read_sta_driver_data, .sta_disassoc = bsd_sta_disassoc, .sta_deauth = bsd_sta_deauth, - .set_freq = hostapd_bsd_set_freq, #else /* HOSTAPD */ .init = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, @@ -1532,6 +1574,7 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .associate = wpa_driver_bsd_associate, .get_capa = wpa_driver_bsd_get_capa, #endif /* HOSTAPD */ + .set_freq = bsd_set_freq, .set_key = bsd_set_key, .set_ieee8021x = bsd_set_ieee8021x, .hapd_set_ssid = bsd_set_ssid, diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c new file mode 100644 index 0000000..009a983 --- /dev/null +++ b/src/drivers/driver_common.c @@ -0,0 +1,89 @@ +/* + * Common driver-related functions + * Copyright (c) 2003-2011, Jouni Malinen + * + * 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 "utils/common.h" +#include "driver.h" + +void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + +const char * event_to_string(enum wpa_event_type event) +{ +#define E2S(n) case EVENT_ ## n: return #n + switch (event) { + E2S(ASSOC); + E2S(DISASSOC); + E2S(MICHAEL_MIC_FAILURE); + E2S(SCAN_RESULTS); + E2S(ASSOCINFO); + E2S(INTERFACE_STATUS); + E2S(PMKID_CANDIDATE); + E2S(STKSTART); + E2S(TDLS); + E2S(FT_RESPONSE); + E2S(IBSS_RSN_START); + E2S(AUTH); + E2S(DEAUTH); + E2S(ASSOC_REJECT); + E2S(AUTH_TIMED_OUT); + E2S(ASSOC_TIMED_OUT); + E2S(FT_RRB_RX); + E2S(WPS_BUTTON_PUSHED); + E2S(TX_STATUS); + E2S(RX_FROM_UNKNOWN); + E2S(RX_MGMT); + E2S(RX_ACTION); + E2S(REMAIN_ON_CHANNEL); + E2S(CANCEL_REMAIN_ON_CHANNEL); + E2S(MLME_RX); + E2S(RX_PROBE_REQ); + E2S(NEW_STA); + E2S(EAPOL_RX); + E2S(SIGNAL_CHANGE); + E2S(INTERFACE_ENABLED); + E2S(INTERFACE_DISABLED); + E2S(CHANNEL_LIST_CHANGED); + E2S(INTERFACE_UNAVAILABLE); + E2S(BEST_CHANNEL); + E2S(UNPROT_DEAUTH); + E2S(UNPROT_DISASSOC); + E2S(STATION_LOW_ACK); + E2S(P2P_DEV_FOUND); + E2S(P2P_GO_NEG_REQ_RX); + E2S(P2P_GO_NEG_COMPLETED); + E2S(P2P_PROV_DISC_REQUEST); + E2S(P2P_PROV_DISC_RESPONSE); + E2S(P2P_SD_REQUEST); + E2S(P2P_SD_RESPONSE); + E2S(IBSS_PEER_LOST); + E2S(DRIVER_GTK_REKEY); + E2S(SCHED_SCAN_STOPPED); + E2S(DRIVER_CLIENT_POLL_OK); + } + + return "UNKNOWN"; +#undef E2S +} diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 952f389..85e9251 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -32,6 +32,7 @@ #include "netlink.h" #include "linux_ioctl.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" /* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X @@ -84,8 +85,8 @@ static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, sa = hdr->addr2; os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.frame = buf; - event.rx_from_unknown.len = len; + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = sa; wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); pos = (u8 *) (hdr + 1); @@ -148,7 +149,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) { struct ieee80211_hdr *hdr; u16 fc, extra_len, type, stype; - unsigned char *extra = NULL; size_t data_len = len; int ver; union wpa_event_data event; @@ -185,7 +185,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) return; } len -= extra_len + 2; - extra = buf + len; } else if (ver == 1 || ver == 2) { handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); return; @@ -289,7 +288,8 @@ static int hostap_send_mlme(void *priv, const u8 *msg, size_t len) static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr) + size_t data_len, int encrypt, const u8 *own_addr, + u32 flags) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr; @@ -763,7 +763,8 @@ static int hostap_set_generic_elem(void *priv, static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { struct hostap_driver_data *drv = priv; @@ -1034,6 +1035,16 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct hostap_driver_data *drv = priv; struct ieee80211_mgmt mgmt; + if (is_broadcast_ether_addr(addr)) { + /* + * New Prism2.5/3 STA firmware versions seem to have issues + * with this broadcast deauth frame. This gets the firmware in + * odd state where nothing works correctly, so let's skip + * sending this for the hostap driver. + */ + return 0; + } + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); @@ -1046,6 +1057,25 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, } +static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; + } + + return 0; +} + + static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { @@ -1114,6 +1144,33 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, return mode; } + +static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, + const u8 *addr, int qos) +{ + struct ieee80211_hdr hdr; + + os_memset(&hdr, 0, sizeof(hdr)); + + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + * This is the reason the driver overrides the default + * handling. + */ + hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + + hdr.frame_control |= + host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr)); +} + #else /* HOSTAPD */ struct wpa_driver_hostap_data { @@ -1306,7 +1363,8 @@ static int wpa_driver_hostap_set_key(const char *ifname, void *priv, HOSTAP_CRYPT_ALG_NAME_LEN); param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; param->u.crypt.idx = key_idx; - os_memcpy(param->u.crypt.seq, seq, seq_len); + if (seq) + os_memcpy(param->u.crypt.seq, seq, seq_len); param->u.crypt.key_len = key_len; os_memcpy((u8 *) (param + 1), key, key_len); @@ -1619,6 +1677,8 @@ const struct wpa_driver_ops wpa_driver_hostap_ops = { .sta_clear_stats = hostap_sta_clear_stats, .get_hw_feature_data = hostap_get_hw_feature_data, .set_ap_wps_ie = hostap_set_ap_wps_ie, + .set_freq = hostap_set_freq, + .poll_client = wpa_driver_hostap_poll_client, #else /* HOSTAPD */ .get_bssid = wpa_driver_hostap_get_bssid, .get_ssid = wpa_driver_hostap_get_ssid, diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c deleted file mode 100644 index 77984f9..0000000 --- a/src/drivers/driver_ipw.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers - * Copyright (c) 2005 Zhu Yi - * Copyright (c) 2004 Lubomir Gelo - * Copyright (c) 2003-2004, Jouni Malinen - * - * 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. - * - * Please note that ipw2100/2200 drivers change to use generic Linux wireless - * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13 - * or newer). driver_wext.c should be used in those cases. - */ - -#include "includes.h" -#include - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_ipw_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */ - -#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 - -#define IPW_CMD_SET_WPA_PARAM 1 -#define IPW_CMD_SET_WPA_IE 2 -#define IPW_CMD_SET_ENCRYPTION 3 -#define IPW_CMD_MLME 4 - -#define IPW_PARAM_WPA_ENABLED 1 -#define IPW_PARAM_TKIP_COUNTERMEASURES 2 -#define IPW_PARAM_DROP_UNENCRYPTED 3 -#define IPW_PARAM_PRIVACY_INVOKED 4 -#define IPW_PARAM_AUTH_ALGS 5 -#define IPW_PARAM_IEEE_802_1X 6 - -#define IPW_MLME_STA_DEAUTH 1 -#define IPW_MLME_STA_DISASSOC 2 - -#define IPW_CRYPT_ERR_UNKNOWN_ALG 2 -#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3 -#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define IPW_CRYPT_ERR_KEY_SET_FAILED 5 -#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7 - -#define IPW_CRYPT_ALG_NAME_LEN 16 - -struct ipw_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u8 name; - u32 value; - } wpa_param; - struct { - u32 len; - u8 reserved[32]; - u8 data[0]; - } wpa_ie; - struct{ - u32 command; - u32 reason_code; - } mlme; - struct { - u8 alg[IPW_CRYPT_ALG_NAME_LEN]; - u8 set_tx; - u32 err; - u8 idx; - u8 seq[8]; - u16 key_len; - u8 key[0]; - } crypt; - - } u; -}; - -/* end of ipw2100.c and ipw2200.c code */ - -static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg); - -static int ipw_ioctl(struct wpa_driver_ipw_data *drv, - struct ipw_param *param, int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]"); - return ret; - } - - return 0; -} - - -static void ipw_show_set_key_error(struct ipw_param *param) -{ - switch (param->u.crypt.err) { - case IPW_CRYPT_ERR_UNKNOWN_ALG: - wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", - param->u.crypt.alg); - wpa_printf(MSG_INFO, "You may need to load kernel module to " - "register that algorithm."); - wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for" - " WEP."); - break; - case IPW_CRYPT_ERR_UNKNOWN_ADDR: - wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", - MAC2STR(param->sta_addr)); - break; - case IPW_CRYPT_ERR_CRYPT_INIT_FAILED: - wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); - break; - case IPW_CRYPT_ERR_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "Key setting failed."); - break; - case IPW_CRYPT_ERR_TX_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "TX key index setting failed."); - break; - case IPW_CRYPT_ERR_CARD_CONF_FAILED: - wpa_printf(MSG_INFO, "Card configuration failed."); - break; - } -} - - -static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct ipw_param *param; - int ret; - size_t blen = sizeof(*param) + wpa_ie_len; - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = IPW_CMD_SET_WPA_IE; - param->u.wpa_ie.len = wpa_ie_len; - os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len); - - ret = ipw_ioctl(drv, param, blen, 1); - - os_free(param); - return ret; -} - - -static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name, - u32 value) -{ - struct ipw_param param; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = IPW_CMD_SET_WPA_PARAM; - param.u.wpa_param.name = name; - param.u.wpa_param.value = value; - - return ipw_ioctl(drv, ¶m, sizeof(param), 1); -} - - -static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr, - int cmd, int reason) -{ - struct ipw_param param; - - os_memset(¶m, 0, sizeof(param)); - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.cmd = IPW_CMD_MLME; - param.u.mlme.command = cmd; - param.u.mlme.reason_code = reason; - - return ipw_ioctl(drv, ¶m, sizeof(param), 1); -} - - -static int wpa_driver_ipw_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - int ret = 0; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - - if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; - - if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0) - ret = -1; - - return ret; -} - - -static int wpa_driver_ipw_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ipw_data *drv = priv; - struct ipw_param *param; - u8 *buf; - size_t blen; - int ret = 0; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct ipw_param *) buf; - param->cmd = IPW_CMD_SET_ENCRYPTION; - os_memset(param->sta_addr, 0xff, ETH_ALEN); - os_strlcpy((char *) param->u.crypt.alg, alg_name, - IPW_CRYPT_ALG_NAME_LEN); - param->u.crypt.set_tx = set_tx ? 1 : 0; - param->u.crypt.idx = key_idx; - os_memcpy(param->u.crypt.seq, seq, seq_len); - param->u.crypt.key_len = key_len; - os_memcpy((u8 *) (param + 1), key, key_len); - - if (ipw_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - ipw_show_set_key_error(param); - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES, - enabled); - -} - - -static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED, - enabled); -} - - -static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ipw_data *drv = priv; - return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code); -} - - -static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ipw_data *drv = priv; - return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code); -} - - -static int -wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ipw_data *drv = priv; - int ret = 0; - int unencrypted_eapol; - - if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted) - < 0) - ret = -1; - if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) - ret = -1; - - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) - unencrypted_eapol = 0; - else - unencrypted_eapol = 1; - - if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X, - unencrypted_eapol) < 0) { - wpa_printf(MSG_DEBUG, "ipw: Failed to configure " - "ieee_802_1x param"); - } - - return ret; -} - - -static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_ipw_data *drv = priv; - int algs = 0; - - if (auth_alg & WPA_AUTH_ALG_OPEN) - algs |= 1; - if (auth_alg & WPA_AUTH_ALG_SHARED) - algs |= 2; - if (auth_alg & WPA_AUTH_ALG_LEAP) - algs |= 4; - if (algs == 0) - algs = 1; /* at least one algorithm should be set */ - - wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs); - return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs); -} - - -static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_ipw_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_ipw_set_operstate(void *priv, int state) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_ipw_init(void *ctx, const char *ifname) -{ - struct wpa_driver_ipw_data *drv; - int ver; - - wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__); - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - ver = wpa_driver_wext_get_version(drv->wext); - if (ver >= 18) { - wpa_printf(MSG_WARNING, "Linux wireless extensions version %d " - "detected.", ver); - wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext " - "(-Dwext) instead of driver_ipw."); - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_driver_ipw_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_driver_ipw_deinit(void *priv) -{ - struct wpa_driver_ipw_data *drv = priv; - wpa_driver_ipw_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_ipw_ops = { - .name = "ipw", - .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 " - "or newer)", - .get_bssid = wpa_driver_ipw_get_bssid, - .get_ssid = wpa_driver_ipw_get_ssid, - .set_key = wpa_driver_ipw_set_key, - .set_countermeasures = wpa_driver_ipw_set_countermeasures, - .scan2 = wpa_driver_ipw_scan, - .get_scan_results2 = wpa_driver_ipw_get_scan_results, - .deauthenticate = wpa_driver_ipw_deauthenticate, - .disassociate = wpa_driver_ipw_disassociate, - .associate = wpa_driver_ipw_associate, - .init = wpa_driver_ipw_init, - .deinit = wpa_driver_ipw_deinit, - .set_operstate = wpa_driver_ipw_set_operstate, -}; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c index 8687404..0a855e7 100644 --- a/src/drivers/driver_madwifi.c +++ b/src/drivers/driver_madwifi.c @@ -69,6 +69,7 @@ #define MADWIFI_NG #endif /* IEEE80211_IOCTL_SETWMMPARAMS */ +#define WPA_KEY_RSC_LEN 8 #ifdef HOSTAPD @@ -461,7 +462,7 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; wk.ik_flags |= IEEE80211_KEY_DEFAULT; @@ -733,6 +734,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); @@ -787,7 +790,8 @@ madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) static int madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, beacon ? wpabuf_len(beacon) : 0, @@ -802,6 +806,24 @@ madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define madwifi_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ +static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; + } + + return 0; +} + static void madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { @@ -846,7 +868,7 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen); + drv_event_assoc(hapd, addr, iebuf, ielen, 0); if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ @@ -1065,7 +1087,7 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv) static int madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr) + int encrypt, const u8 *own_addr, u32 flags) { struct madwifi_driver_data *drv = priv; unsigned char buf[3000]; @@ -1509,8 +1531,7 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, wk.ik_keyix = key_idx; wk.ik_keylen = key_len; #ifdef WORDS_BIGENDIAN -#define WPA_KEY_RSC_LEN 8 - { + if (seq) { size_t i; u8 tmp[WPA_KEY_RSC_LEN]; os_memset(tmp, 0, sizeof(tmp)); @@ -1519,7 +1540,8 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); } #else /* WORDS_BIGENDIAN */ - os_memcpy(&wk.ik_keyrsc, seq, seq_len); + if (seq) + os_memcpy(&wk.ik_keyrsc, seq, seq_len); #endif /* WORDS_BIGENDIAN */ os_memcpy(wk.ik_keydata, key, key_len); @@ -1838,6 +1860,7 @@ const struct wpa_driver_ops wpa_driver_madwifi_ops = { .sta_clear_stats = madwifi_sta_clear_stats, .commit = madwifi_commit, .set_ap_wps_ie = madwifi_set_ap_wps_ie, + .set_freq = madwifi_set_freq, #else /* HOSTAPD */ .get_bssid = wpa_driver_madwifi_get_bssid, .get_ssid = wpa_driver_madwifi_get_ssid, diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 462dd81..dbe9a28 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -1001,8 +1001,7 @@ static int wpa_driver_ndis_set_key(const char *ifname, void *priv, int res, pairwise; u8 bssid[ETH_ALEN]; - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { /* Group Key */ pairwise = 0; if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) @@ -1066,6 +1065,7 @@ wpa_driver_ndis_associate(void *priv, { struct wpa_driver_ndis_data *drv = priv; u32 auth_mode, encr, priv_mode, mode; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; drv->mode = params->mode; @@ -1091,7 +1091,6 @@ wpa_driver_ndis_associate(void *priv, if (params->key_mgmt_suite == KEY_MGMT_NONE || params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { /* Re-set WEP keys if static WEP configuration is used. */ - u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int i; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) @@ -1125,6 +1124,22 @@ wpa_driver_ndis_associate(void *priv, } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; + if (params->wps == WPS_MODE_PRIVACY) { + u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 }; + /* + * Some NDIS drivers refuse to associate in open mode + * configuration due to Privacy field mismatch, so use + * a workaround to make the configuration look like + * matching one for WPS provisioning. + */ + wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a " + "workaround to allow driver to associate " + "for WPS"); + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, 0, 1, + NULL, 0, dummy_key, + sizeof(dummy_key)); + } #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; @@ -1148,6 +1163,12 @@ wpa_driver_ndis_associate(void *priv, encr = Ndis802_11Encryption1Enabled; break; case CIPHER_NONE: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ if (params->group_suite == CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; else if (params->group_suite == CIPHER_TKIP) @@ -1156,7 +1177,14 @@ wpa_driver_ndis_associate(void *priv, encr = Ndis802_11EncryptionDisabled; break; default: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ encr = Ndis802_11EncryptionDisabled; + break; }; if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, @@ -3185,94 +3213,33 @@ wpa_driver_ndis_get_interfaces(void *global_priv) } -const struct wpa_driver_ops wpa_driver_ndis_ops = { - "ndis", - "Windows NDIS driver", - wpa_driver_ndis_get_bssid, - wpa_driver_ndis_get_ssid, - wpa_driver_ndis_set_key, - wpa_driver_ndis_init, - wpa_driver_ndis_deinit, - NULL /* set_param */, - NULL /* set_countermeasures */, - wpa_driver_ndis_deauthenticate, - wpa_driver_ndis_disassociate, - wpa_driver_ndis_associate, - wpa_driver_ndis_add_pmkid, - wpa_driver_ndis_remove_pmkid, - wpa_driver_ndis_flush_pmkid, - wpa_driver_ndis_get_capa, - wpa_driver_ndis_poll, - wpa_driver_ndis_get_ifname, - wpa_driver_ndis_get_mac_addr, - NULL /* send_eapol */, - NULL /* set_operstate */, - NULL /* mlme_setprotection */, - NULL /* get_hw_feature_data */, - NULL /* set_channel */, - NULL /* set_ssid */, - NULL /* set_bssid */, - NULL /* send_mlme */, - NULL /* mlme_add_sta */, - NULL /* mlme_remove_sta */, - NULL /* update_ft_ies */, - NULL /* send_ft_action */, - wpa_driver_ndis_get_scan_results, - NULL /* set_country */, - NULL /* global_init */, - NULL /* global_deinit */, - NULL /* init2 */, - wpa_driver_ndis_get_interfaces, - wpa_driver_ndis_scan, - NULL /* authenticate */, - NULL /* set_beacon */, - NULL /* hapd_init */, - NULL /* hapd_deinit */, - NULL /* set_ieee8021x */, - NULL /* set_privacy */, - NULL /* get_seqnum */, - NULL /* flush */, - NULL /* set_generic_elem */, - NULL /* read_sta_data */, - NULL /* hapd_send_eapol */, - NULL /* sta_deauth */, - NULL /* sta_disassoc */, - NULL /* sta_remove */, - NULL /* hapd_get_ssid */, - NULL /* hapd_set_ssid */, - NULL /* hapd_set_countermeasures */, - NULL /* sta_add */, - NULL /* get_inact_sec */, - NULL /* sta_clear_stats */, - NULL /* set_freq */, - NULL /* set_rts */, - NULL /* set_frag */, - NULL /* sta_set_flags */, - NULL /* set_rate_sets */, - NULL /* set_cts_protect */, - NULL /* set_preamble */, - NULL /* set_short_slot_time */, - NULL /* set_tx_queue_params */, - NULL /* valid_bss_mask */, - NULL /* if_add */, - NULL /* if_remove */, - NULL /* set_sta_vlan */, - NULL /* commit */, - NULL /* send_ether */, - NULL /* set_radius_acl_auth */, - NULL /* set_radius_acl_expire */, - NULL /* set_ht_params */, - NULL /* set_ap_wps_ie */, - NULL /* set_supp_port */, - NULL /* set_wds_sta */, - NULL /* send_action */, - NULL /* remain_on_channel */, - NULL /* cancel_remain_on_channel */, - NULL /* probe_req_report */, - NULL /* disable_11b_rates */, - NULL /* deinit_ap */, - NULL /* suspend */, - NULL /* resume */, - NULL /* signal_monitor */, - NULL /* send_frame */ -}; +static const char *ndis_drv_name = "ndis"; +static const char *ndis_drv_desc = "Windows NDIS driver"; + +struct wpa_driver_ops wpa_driver_ndis_ops; + +void driver_ndis_init_ops(void) +{ + os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops)); + wpa_driver_ndis_ops.name = ndis_drv_name; + wpa_driver_ndis_ops.desc = ndis_drv_desc; + wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid; + wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid; + wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key; + wpa_driver_ndis_ops.init = wpa_driver_ndis_init; + wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; + wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; + wpa_driver_ndis_ops.disassociate = wpa_driver_ndis_disassociate; + wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; + wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; + wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; + wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid; + wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa; + wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll; + wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname; + wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr; + wpa_driver_ndis_ops.get_scan_results2 = + wpa_driver_ndis_get_scan_results; + wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces; + wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan; +} diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c deleted file mode 100644 index cd2f61e..0000000 --- a/src/drivers/driver_ndiswrapper.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ndiswrapper - * Copyright (c) 2004-2006, Giridhar Pemmasani - * Copyright (c) 2004-2006, Jouni Malinen - * - * 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. - * - * Please note that ndiswrapper supports WPA configuration via Linux wireless - * extensions and if the kernel includes support for this, driver_wext.c should - * be used instead of this driver wrapper. - */ - -#include "includes.h" -#include - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" - -struct wpa_driver_ndiswrapper_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - - -struct wpa_key { - enum wpa_alg alg; - const u8 *addr; - int key_index; - int set_tx; - const u8 *seq; - size_t seq_len; - const u8 *key; - size_t key_len; -}; - -struct wpa_assoc_info { - const u8 *bssid; - const u8 *ssid; - size_t ssid_len; - int freq; - const u8 *wpa_ie; - size_t wpa_ie_len; - enum wpa_cipher pairwise_suite; - enum wpa_cipher group_suite; - enum wpa_key_mgmt key_mgmt_suite; - int auth_alg; - int mode; -}; - -#define PRIV_RESET SIOCIWFIRSTPRIV+0 -#define WPA_SET_WPA SIOCIWFIRSTPRIV+1 -#define WPA_SET_KEY SIOCIWFIRSTPRIV+2 -#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3 -#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4 -#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5 -#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6 -#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7 -#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8 -#define WPA_INIT SIOCIWFIRSTPRIV+9 -#define WPA_DEINIT SIOCIWFIRSTPRIV+10 -#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 - -static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg); - -static int get_socket(void) -{ - static const int families[] = { - AF_INET, AF_IPX, AF_AX25, AF_APPLETALK - }; - unsigned int i; - int sock; - - for (i = 0; i < sizeof(families) / sizeof(int); ++i) { - sock = socket(families[i], SOCK_DGRAM, 0); - if (sock >= 0) - return sock; - } - - return -1; -} - -static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request, - struct iwreq *pwrq) -{ - os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ); - return ioctl(drv->sock, request, pwrq); -} - -static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - struct iwreq priv_req; - int ret = 0; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.data.flags = enabled; - if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - struct wpa_key wpa_key; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - wpa_key.alg = alg; - wpa_key.addr = addr; - wpa_key.key_index = key_idx; - wpa_key.set_tx = set_tx; - wpa_key.seq = seq; - wpa_key.seq_len = seq_len; - wpa_key.key = key; - wpa_key.key_len = key_len; - - priv_req.u.data.pointer = (void *)&wpa_key; - priv_req.u.data.length = sizeof(wpa_key); - - if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0) - ret = -1; - - if (alg == WPA_ALG_NONE) { - /* - * ndiswrapper did not seem to be clearing keys properly in - * some cases with WPA_SET_KEY. For example, roaming from WPA - * enabled AP to plaintext one seemed to fail since the driver - * did not associate. Try to make sure the keys are cleared so - * that plaintext APs can be used in all cases. - */ - wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx, - set_tx, seq, seq_len, key, key_len); - } - - return ret; -} - -static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = enabled; - if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0) - ret = -1; - - return ret; -} - -static int wpa_ndiswrapper_set_drop_unencrypted(void *priv, - int enabled) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = enabled; - if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = reason_code; - os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); - if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); - if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int -wpa_ndiswrapper_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct wpa_assoc_info wpa_assoc_info; - struct iwreq priv_req; - - if (wpa_ndiswrapper_set_drop_unencrypted(drv, - params->drop_unencrypted) < 0) - ret = -1; - if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - - os_memset(&priv_req, 0, sizeof(priv_req)); - os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); - - wpa_assoc_info.bssid = params->bssid; - wpa_assoc_info.ssid = params->ssid; - wpa_assoc_info.ssid_len = params->ssid_len; - wpa_assoc_info.freq = params->freq; - wpa_assoc_info.wpa_ie = params->wpa_ie; - wpa_assoc_info.wpa_ie_len = params->wpa_ie_len; - wpa_assoc_info.pairwise_suite = params->pairwise_suite; - wpa_assoc_info.group_suite = params->group_suite; - wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite; - wpa_assoc_info.auth_alg = params->auth_alg; - wpa_assoc_info.mode = params->mode; - - priv_req.u.data.pointer = (void *)&wpa_assoc_info; - priv_req.u.data.length = sizeof(wpa_assoc_info); - - if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.param.value = auth_alg; - if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0) - ret = -1; - return ret; -} - -static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_ndiswrapper_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, params); -} - - -static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - int ret = 0; - struct iwreq priv_req; - - os_memset(&priv_req, 0, sizeof(priv_req)); - - priv_req.u.data.pointer = (void *) capa; - priv_req.u.data.length = sizeof(*capa); - if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0) - ret = -1; - return ret; - -} - - -static int wpa_ndiswrapper_set_operstate(void *priv, int state) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) -{ - struct wpa_driver_ndiswrapper_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = get_socket(); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - wpa_ndiswrapper_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_ndiswrapper_deinit(void *priv) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - wpa_ndiswrapper_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { - .name = "ndiswrapper", - .desc = "Linux ndiswrapper (deprecated; use wext)", - .set_key = wpa_ndiswrapper_set_key, - .set_countermeasures = wpa_ndiswrapper_set_countermeasures, - .deauthenticate = wpa_ndiswrapper_deauthenticate, - .disassociate = wpa_ndiswrapper_disassociate, - .associate = wpa_ndiswrapper_associate, - - .get_bssid = wpa_ndiswrapper_get_bssid, - .get_ssid = wpa_ndiswrapper_get_ssid, - .scan2 = wpa_ndiswrapper_scan, - .get_scan_results2 = wpa_ndiswrapper_get_scan_results, - .init = wpa_ndiswrapper_init, - .deinit = wpa_ndiswrapper_deinit, - .get_capa = wpa_ndiswrapper_get_capa, - .set_operstate = wpa_ndiswrapper_set_operstate, -}; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 364158c..e12e511 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -18,31 +18,138 @@ #include "includes.h" #include +#include +#include +#include #include #include #include #include +#include #include #include #include "nl80211_copy.h" #include "common.h" #include "eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} + +static inline int __genl_ctrl_alloc_cache(struct nl_handle *h, + struct nl_cache **cache) +{ + struct nl_cache *tmp = genl_ctrl_alloc_cache(h); + if (!tmp) + return -ENOMEM; + *cache = tmp; + return 0; +} +#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache #endif /* CONFIG_LIBNL20 */ +struct nl80211_handles { + struct nl_handle *handle; + struct nl_cache *cache; +}; + + +static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb, + const char *dbg) +{ + if (!handles) + return -1; + + handles->handle = nl80211_handle_alloc(cb); + if (handles->handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks (%s)", dbg); + return -1; + } + + if (genl_connect(handles->handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink (%s)", dbg); + goto err; + } + + if (genl_ctrl_alloc_cache(handles->handle, &handles->cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache (%s)", dbg); + goto err; + } + + return 0; +err: + nl80211_handle_destroy(handles->handle); + return -1; +} + + +static void nl_destroy_handles(struct nl80211_handles *handles) +{ + if (handles->handle == NULL) + return; + nl_cache_free(handles->cache); + nl80211_handle_destroy(handles->handle); + handles->handle = NULL; +} + + #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ #endif @@ -57,21 +164,41 @@ #define IF_OPER_UP 6 #endif +struct nl80211_global { + struct dl_list interfaces; + int if_add_ifindex; + struct netlink_data *netlink; + struct nl_cb *nl_cb; + struct nl80211_handles nl; + struct genl_family *nl80211; + int ioctl_sock; /* socket for ioctl() use */ +}; + +static void nl80211_global_deinit(void *priv); +static void wpa_driver_nl80211_deinit(void *priv); + struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; unsigned int beacon_set:1; + unsigned int added_if_into_bridge:1; + unsigned int added_bridge:1; }; struct wpa_driver_nl80211_data { + struct nl80211_global *global; + struct dl_list list; + u8 addr[ETH_ALEN]; + char phyname[32]; void *ctx; - struct netlink_data *netlink; - int ioctl_sock; /* socket for ioctl() use */ - char brname[IFNAMSIZ]; int ifindex; int if_removed; + int if_disabled; + int ignore_if_down_event; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; int has_capability; @@ -79,39 +206,40 @@ struct wpa_driver_nl80211_data { int scan_complete_events; - struct nl_handle *nl_handle; - struct nl_handle *nl_handle_event; - struct nl_cache *nl_cache; - struct nl_cache *nl_cache_event; - struct nl_cb *nl_cb; - struct genl_family *nl80211; + struct nl80211_handles nl_event, nl_preq; u8 auth_bssid[ETH_ALEN]; u8 bssid[ETH_ALEN]; int associated; u8 ssid[32]; size_t ssid_len; - int nlmode; - int ap_scan_as_station; + enum nl80211_iftype nlmode; + enum nl80211_iftype ap_scan_as_station; unsigned int assoc_freq; int monitor_sock; int monitor_ifidx; - int probe_req_report; - int disable_11b_rates; + int no_monitor_iface_capab; + unsigned int disabled_11b_rates:1; unsigned int pending_remain_on_chan:1; - unsigned int added_bridge:1; - unsigned int added_if_into_bridge:1; + unsigned int in_interface_list:1; u64 remain_on_chan_cookie; u64 send_action_cookie; + unsigned int last_mgmt_freq; + unsigned int ap_oper_freq; + struct wpa_driver_scan_filter *filter_ssids; size_t num_filter_ssids; struct i802_bss first_bss; +#ifdef CONFIG_AP + struct l2_packet_data *l2; +#endif /* CONFIG_AP */ + #ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ @@ -127,7 +255,8 @@ struct wpa_driver_nl80211_data { static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); -static int wpa_driver_nl80211_set_mode(void *priv, int mode); +static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, @@ -135,6 +264,11 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); +static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, u64 *cookie, + int no_cck); +static int wpa_driver_nl80211_probe_req_report(void *priv, int report); #ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); @@ -144,18 +278,57 @@ static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname); #else /* HOSTAPD */ -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ +} + +static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ +} + +static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { return 0; } #endif /* HOSTAPD */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); + + +static int is_ap_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_AP || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +static int is_sta_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_STATION || + nlmode == NL80211_IFTYPE_P2P_CLIENT); +} + + +static int is_p2p_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_P2P_CLIENT || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; + unsigned int assoc_freq; + u8 assoc_bssid[ETH_ALEN]; +}; + +static int bss_info_handler(struct nl_msg *msg, void *arg); + /* nl80211 code */ static int ack_handler(struct nl_msg *msg, void *arg) @@ -195,7 +368,7 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv, struct nl_cb *cb; int err = -ENOMEM; - cb = nl_cb_clone(drv->nl_cb); + cb = nl_cb_clone(drv->global->nl_cb); if (!cb) goto out; @@ -227,7 +400,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { - return send_and_recv(drv, drv->nl_handle, msg, valid_handler, + return send_and_recv(drv, drv->global->nl.handle, msg, valid_handler, valid_data); } @@ -279,7 +452,8 @@ static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + genlmsg_put(msg, 0, 0, + genl_ctrl_resolve(drv->global->nl.handle, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); @@ -294,6 +468,14 @@ nla_put_failure: } +static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) +{ + return genlmsg_put(msg, 0, 0, genl_family_get_id(drv->global->nl80211), + 0, flags, cmd, 0); +} + + static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { struct i802_bss *bss = priv; @@ -334,10 +516,28 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, del ? "removed" : "added"); if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { - if (del) + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already set - ignore event"); + return; + } drv->if_removed = 1; - else + } else { + if (if_nametoindex(drv->first_bss.ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s " + "does not exist - ignore " + "RTM_NEWLINK", + drv->first_bss.ifname); + return; + } + if (!drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already cleared - ignore event"); + return; + } drv->if_removed = 0; + } } wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); @@ -387,17 +587,33 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, } +static struct wpa_driver_nl80211_data * +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || + have_ifidx(drv, idx)) + return drv; + } + return NULL; +} + + static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = ctx; + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; int attrlen, rta_len; struct rtattr *attr; u32 brid = 0; + char namebuf[IFNAMSIZ]; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && - !have_ifidx(drv, ifi->ifi_index)) { + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " "ifindex %d", ifi->ifi_index); return; @@ -410,6 +626,50 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss.ifname) > 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event since interface %s is up", namebuf); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + if (drv->ignore_if_down_event) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event generated by mode change"); + drv->ignore_if_down_event = 0; + } else { + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, + EVENT_INTERFACE_DISABLED, NULL); + } + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss.ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s is down", + namebuf); + } else if (if_nametoindex(drv->first_bss.ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s does not exist", + drv->first_bss.ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s is marked " + "removed", drv->first_bss.ifname); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -419,7 +679,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) - netlink_send_oper_ifla(drv->netlink, drv->ifindex, + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, IF_OPER_UP); attrlen = len; @@ -436,16 +696,13 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, attr = RTA_NEXT(attr, attrlen); } -#ifdef HOSTAPD if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been added to bridge */ - char namebuf[IFNAMSIZ]; if_indextoname(brid, namebuf); wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); add_ifidx(drv, brid); } -#endif /* HOSTAPD */ } @@ -453,11 +710,19 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = ctx; + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; int attrlen, rta_len; struct rtattr *attr; u32 brid = 0; + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for " + "foreign ifindex %d", ifi->ifi_index); + return; + } + attrlen = len; attr = (struct rtattr *) buf; @@ -473,7 +738,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, attr = RTA_NEXT(attr, attrlen); } -#ifdef HOSTAPD if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; @@ -482,7 +746,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, "%s", brid, namebuf); del_ifidx(drv, brid); } -#endif /* HOSTAPD */ } @@ -513,6 +776,37 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, } +static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret; + struct nl80211_bss_info_arg arg; + + os_memset(&arg, 0, sizeof(arg)); + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " + "associated BSS from scan results: %u MHz", + arg.assoc_freq); + return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return drv->assoc_freq; +} + + static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const u8 *frame, size_t len) { @@ -530,6 +824,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) { os_memset(&event, 0, sizeof(event)); + event.assoc_reject.bssid = mgmt->bssid; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_reject.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; @@ -578,6 +873,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, os_memset(&event, 0, sizeof(event)); if (cmd == NL80211_CMD_CONNECT && nla_get_u16(status) != WLAN_STATUS_SUCCESS) { + if (addr) + event.assoc_reject.bssid = nla_data(addr); if (resp_ie) { event.assoc_reject.resp_ies = nla_data(resp_ie); event.assoc_reject.resp_ies_len = nla_len(resp_ie); @@ -600,10 +897,35 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.resp_ies_len = nla_len(resp_ie); } + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr) +{ + union wpa_event_data data; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two disassociation events that could + * confuse the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event when using userspace SME"); + return; + } + + drv->associated = 0; + os_memset(&data, 0, sizeof(data)); + if (reason) + data.disassoc_info.reason_code = nla_get_u16(reason); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); +} + + static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *addr) { @@ -629,8 +951,8 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, } -static void mlme_event_action(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, const u8 *frame, size_t len) +static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; @@ -646,15 +968,23 @@ static void mlme_event_action(struct wpa_driver_nl80211_data *drv, stype = WLAN_FC_GET_STYPE(fc); os_memset(&event, 0, sizeof(event)); - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category + 1; - event.rx_action.len = frame + len - event.rx_action.data; - if (freq) + if (freq) { event.rx_action.freq = nla_get_u32(freq); - wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + drv->last_mgmt_freq = event.rx_action.freq; + } + if (stype == WLAN_FC_STYPE_ACTION) { + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + } else { + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } } @@ -671,10 +1001,11 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, return; cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s", + wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s " + "(ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)"); + " (match)" : " (unknown)", ack != NULL); if (cookie_val != drv->send_action_cookie) return; @@ -730,9 +1061,51 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, if (type == EVENT_DISASSOC) { event.disassoc_info.addr = bssid; event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } } else { event.deauth_info.addr = bssid; event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 reason_code = 0; + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + os_memset(&event, 0, sizeof(event)); + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; } wpa_supplicant_event(drv->ctx, type, &event); @@ -775,13 +1148,21 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION: - mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + case NL80211_CMD_FRAME: + mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event_action_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; default: break; } @@ -877,7 +1258,8 @@ static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, if (cookie != drv->remain_on_chan_cookie) return; /* not for us */ - drv->pending_remain_on_chan = !cancel_event; + if (cancel_event) + drv->pending_remain_on_chan = 0; os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = freq; @@ -929,78 +1311,458 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } -static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) +static int get_link_signal(struct nl_msg *msg, void *arg) { - static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, }; - struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; - enum nl80211_cqm_rssi_threshold_event event; - union wpa_event_data ed; - - if (tb[NL80211_ATTR_CQM] == NULL || - nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], - cqm_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); - return; - } - - if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) - return; - event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + struct wpa_signal_info *sig_change = arg; - os_memset(&ed, 0, sizeof(ed)); + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; - if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI high"); - ed.signal_change.above_threshold = 1; - } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI low"); - ed.signal_change.above_threshold = 0; - } else - return; + sig_change->current_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) { + sig_change->current_txrate = 0; + } else { + if (rinfo[NL80211_RATE_INFO_BITRATE]) { + sig_change->current_txrate = + nla_get_u16(rinfo[ + NL80211_RATE_INFO_BITRATE]) * 100; + } + } + } - wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); + return NL_SKIP; } -static int process_event(struct nl_msg *msg, void *arg) +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) { - struct wpa_driver_nl80211_data *drv = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - union wpa_event_data data; + struct nl_msg *msg; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + sig->current_signal = -9999; + sig->current_txrate = 0; - if (tb[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_link_noise(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " + "attributes!"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + sig_change->frequency) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + sig_change->current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + + return NL_SKIP; +} + + +static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change) +{ + struct nl_msg *msg; + + sig_change->current_noise = 9999; + sig_change->frequency = drv->assoc_freq; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; + struct wpa_signal_info sig; + int res; + + if (tb[NL80211_ATTR_CQM] == NULL || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], + cqm_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); + return; + } + + os_memset(&ed, 0, sizeof(ed)); + + if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (!tb[NL80211_ATTR_MAC]) + return; + os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + return; + event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + + if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI high"); + ed.signal_change.above_threshold = 1; + } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI low"); + ed.signal_change.above_threshold = 0; + } else + return; + + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); +} + + +static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) { + u8 *ies = NULL; + size_t ies_len = 0; + if (tb[NL80211_ATTR_IE]) { + ies = nla_data(tb[NL80211_ATTR_IE]); + ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); + drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); +} + + +static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, + MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) { + drv_event_disassoc(drv->ctx, addr); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); +} + + +static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; + static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { + .minlen = NL80211_KEK_LEN, + .maxlen = NL80211_KEK_LEN, + }, + [NL80211_REKEY_DATA_KCK] = { + .minlen = NL80211_KCK_LEN, + .maxlen = NL80211_KCK_LEN, + }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { + .minlen = NL80211_REPLAY_CTR_LEN, + .maxlen = NL80211_REPLAY_CTR_LEN, + }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_MAC]) + return; + if (!tb[NL80211_ATTR_REKEY_DATA]) + return; + if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, + tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) + return; + if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) + return; + + os_memset(&data, 0, sizeof(data)); + data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, + MAC2STR(data.driver_gtk_rekey.bssid)); + data.driver_gtk_rekey.replay_ctr = + nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); + wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", + data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); +} + + +static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; + static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { + [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, + [NL80211_PMKSA_CANDIDATE_BSSID] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) + return; + if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, + tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) + return; + if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || + !cand[NL80211_PMKSA_CANDIDATE_BSSID]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, + nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); + data.pmkid_candidate.index = + nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); + data.pmkid_candidate.preauth = + cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static int process_event(struct nl_msg *msg, void *arg) +{ + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" " for foreign interface (ifindex %d)", gnlh->cmd, ifindex); return NL_SKIP; } } - if (drv->ap_scan_as_station && + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS || gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) { wpa_driver_nl80211_set_mode(&drv->first_bss, - IEEE80211_MODE_AP); - drv->ap_scan_as_station = 0; + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } switch (gnlh->cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); break; + case NL80211_CMD_START_SCHED_SCAN: + wpa_printf(MSG_DEBUG, "nl80211: Sched scan started"); + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped"); + wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + break; case NL80211_CMD_NEW_SCAN_RESULTS: wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); drv->scan_complete_events = 1; @@ -1008,6 +1770,11 @@ static int process_event(struct nl_msg *msg, void *arg) drv->ctx); send_scan_event(drv, 0, tb); break; + case NL80211_CMD_SCHED_SCAN_RESULTS: + wpa_printf(MSG_DEBUG, + "nl80211: New sched scan results available"); + send_scan_event(drv, 0, tb); + break; case NL80211_CMD_SCAN_ABORTED: wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); /* @@ -1022,8 +1789,10 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_ACTION: - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], @@ -1038,21 +1807,8 @@ static int process_event(struct nl_msg *msg, void *arg) tb[NL80211_ATTR_RESP_IE]); break; case NL80211_CMD_DISCONNECT: - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two disassociation events that could - * confuse the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event when using userspace SME"); - break; - } - drv->associated = 0; - os_memset(&data, 0, sizeof(data)); - if (tb[NL80211_ATTR_REASON_CODE]) - data.disassoc_info.reason_code = - nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); + mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], + tb[NL80211_ATTR_MAC]); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(drv, tb); @@ -1069,6 +1825,28 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_NOTIFY_CQM: nl80211_cqm_event(drv, tb); break; + case NL80211_CMD_REG_CHANGE: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + break; + case NL80211_CMD_NEW_STATION: + nl80211_new_station_event(drv, tb); + break; + case NL80211_CMD_DEL_STATION: + nl80211_del_station_event(drv, tb); + break; + case NL80211_CMD_SET_REKEY_OFFLOAD: + nl80211_rekey_offload_event(drv, tb); + break; + case NL80211_CMD_PMKSA_CANDIDATE: + nl80211_pmksa_candidate_event(drv, tb); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); @@ -1080,19 +1858,19 @@ static int process_event(struct nl_msg *msg, void *arg) static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) + void *handle) { struct nl_cb *cb; struct wpa_driver_nl80211_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "nl80211: Event message available"); - cb = nl_cb_clone(drv->nl_cb); + cb = nl_cb_clone(drv->global->nl_cb); if (!cb) return; nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); - nl_recvmsgs(drv->nl_handle_event, cb); + nl_recvmsgs(handle, cb); nl_cb_put(cb); } @@ -1121,24 +1899,22 @@ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) alpha2[1] = alpha2_arg[1]; alpha2[2] = '\0'; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_REQ_SET_REG, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); if (send_and_recv_msgs(drv, msg, NULL, NULL)) return -EINVAL; return 0; nla_put_failure: + nlmsg_free(msg); return -EINVAL; } -#ifndef HOSTAPD struct wiphy_info_data { - int max_scan_ssids; - int ap_supported; - int auth_supported; - int connect_supported; + struct wpa_driver_capa *capa; + + unsigned int error:1; }; @@ -1147,37 +1923,181 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct wiphy_info_data *info = arg; + int p2p_go_supported = 0, p2p_client_supported = 0; + int p2p_concurrent = 0; + int auth_supported = 0, connect_supported = 0; + struct wpa_driver_capa *capa = info->capa; + static struct nla_policy + iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + }, + iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) - info->max_scan_ssids = + capa->max_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { struct nlattr *nl_mode; int i; nla_for_each_nested(nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - if (nl_mode->nla_type == NL80211_IFTYPE_AP) { - info->ap_supported = 1; + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_P2P_GO: + p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + p2p_client_supported = 1; break; } } } + if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) { + struct nlattr *nl_combi; + int rem_combi; + + nla_for_each_nested(nl_combi, + tb[NL80211_ATTR_INTERFACE_COMBINATIONS], + rem_combi) { + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, + iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + goto broken_combination; + + nla_for_each_nested(nl_limit, + tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, + MAX_NL80211_IFACE_LIMIT, + nl_limit, + iface_limit_policy); + if (err || + !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + goto broken_combination; + + nla_for_each_nested( + nl_mode, + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; + } + + if (combination_has_p2p && combination_has_mgd) { + p2p_concurrent = 1; + break; + } + +broken_combination: + ; + } + } + if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { struct nlattr *nl_cmd; int i; nla_for_each_nested(nl_cmd, tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { - u32 cmd = nla_get_u32(nl_cmd); - if (cmd == NL80211_CMD_AUTHENTICATE) - info->auth_supported = 1; - else if (cmd == NL80211_CMD_CONNECT) - info->connect_supported = 1; + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + /* + * Disabled for 1.x for now as it is + * broken there due to the way it ends + * up getting used. + capa->sched_scan_supported = 1; + */ + break; + } + } + } + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + /* default to 5000 since early versions of mac80211 don't set it */ + capa->max_remain_on_chan = 5000; + + if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]) + capa->max_remain_on_chan = + nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (auth_supported) + capa->flags |= WPA_DRIVER_FLAGS_SME; + else if (!connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + info->error = 1; + } + + if (p2p_go_supported && p2p_client_supported) + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + + if (tb[NL80211_ATTR_TDLS_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= + WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; } } @@ -1191,12 +2111,13 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg; os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); @@ -1214,6 +2135,10 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) struct wiphy_info_data info; if (wpa_driver_nl80211_get_info(drv, &info)) return -1; + + if (info.error) + return -1; + drv->has_capability = 1; /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | @@ -1228,103 +2153,88 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; - drv->capa.max_scan_ssids = info.max_scan_ssids; - if (info.ap_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_AP; - - if (info.auth_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_SME; - else if (!info.connect_supported) { - wpa_printf(MSG_INFO, "nl80211: Driver does not support " - "authentication/association or connect commands"); - return -1; - } - + drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; - drv->capa.max_remain_on_chan = 5000; + drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; return 0; } -#endif /* HOSTAPD */ -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, - void *ctx) +#ifdef ANDROID +static int android_genl_ctrl_resolve(struct nl_handle *handle, + const char *name) { - int ret; - - /* Initialize generic netlink and nl80211 */ + /* + * Android ICS has very minimal genl_ctrl_resolve() implementation, so + * need to work around that. + */ + struct nl_cache *cache = NULL; + struct genl_family *nl80211 = NULL; + int id = -1; - drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (drv->nl_cb == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks"); - goto err1; + if (genl_ctrl_alloc_cache(handle, &cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto fail; } - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); - if (drv->nl_handle == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks"); - goto err2; - } + nl80211 = genl_ctrl_search_by_name(cache, name); + if (nl80211 == NULL) + goto fail; - drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); - if (drv->nl_handle_event == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks (event)"); - goto err2b; - } + id = genl_family_get_id(nl80211); - if (genl_connect(drv->nl_handle)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " - "netlink"); - goto err3; - } +fail: + if (nl80211) + genl_family_put(nl80211); + if (cache) + nl_cache_free(cache); - if (genl_connect(drv->nl_handle_event)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " - "netlink (event)"); - goto err3; - } + return id; +} +#define genl_ctrl_resolve android_genl_ctrl_resolve +#endif /* ANDROID */ -#ifdef CONFIG_LIBNL20 - if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto err3; - } - if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) < - 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache (event)"); - goto err3b; - } -#else /* CONFIG_LIBNL20 */ - drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); - if (drv->nl_cache == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto err3; - } - drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event); - if (drv->nl_cache_event == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache (event)"); - goto err3b; + +static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) +{ + global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (global->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + return -1; } -#endif /* CONFIG_LIBNL20 */ - drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); - if (drv->nl80211 == NULL) { + if (nl_create_handles(&global->nl, global->nl_cb, "nl")) + return -1; + + global->nl80211 = genl_ctrl_search_by_name(global->nl.cache, + "nl80211"); + if (global->nl80211 == NULL) { wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " "found"); - goto err4; + return -1; } + return 0; +} + + +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) +{ + struct nl80211_global *global = drv->global; + int ret; + + /* Initialize generic netlink and nl80211 */ + + if (nl_create_handles(&drv->nl_event, global->nl_cb, "event")) + goto err3; + ret = nl_get_multicast_id(drv, "nl80211", "scan"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle_event, ret); + ret = nl_socket_add_membership(drv->nl_event.handle, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for scan events: %d (%s)", @@ -1334,7 +2244,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, ret = nl_get_multicast_id(drv, "nl80211", "mlme"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle_event, ret); + ret = nl_socket_add_membership(drv->nl_event.handle, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for mlme events: %d (%s)", @@ -1342,95 +2252,169 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, goto err4; } - eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), - wpa_driver_nl80211_event_receive, drv, ctx); + ret = nl_get_multicast_id(drv, "nl80211", "regulatory"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_event.handle, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for regulatory events: %d (%s)", + ret, strerror(-ret)); + /* Continue without regulatory events */ + } + + eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle), + wpa_driver_nl80211_event_receive, drv, + drv->nl_event.handle); return 0; err4: - nl_cache_free(drv->nl_cache_event); -err3b: - nl_cache_free(drv->nl_cache); + nl_destroy_handles(&drv->nl_event); err3: - nl_handle_destroy(drv->nl_handle_event); -err2b: - nl_handle_destroy(drv->nl_handle); -err2: - nl_cb_put(drv->nl_cb); -err1: return -1; } +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (linux_set_iface_flags(drv->global->ioctl_sock, + drv->first_bss.ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->first_bss.ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + drv->first_bss.ifname, drv->phyname); +} + + +#ifdef CONFIG_AP +static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u", + (unsigned int) len); +} +#endif /* CONFIG_AP */ + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() * Returns: Pointer to private data, %NULL on failure */ -static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, + void *global_priv) { struct wpa_driver_nl80211_data *drv; - struct netlink_config *cfg; + struct rfkill_config *rcfg; struct i802_bss *bss; + if (global_priv == NULL) + return NULL; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; + drv->global = global_priv; drv->ctx = ctx; bss = &drv->first_bss; bss->drv = drv; os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); drv->monitor_ifidx = -1; drv->monitor_sock = -1; - drv->ioctl_sock = -1; + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - if (wpa_driver_nl80211_init_nl(drv, ctx)) { + if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); - goto failed; - } + nl80211_get_phy_name(drv); - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - goto failed; - cfg->ctx = drv; - cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; - cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); } + if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; - return bss; +#ifdef CONFIG_AP + drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL, + nl80211_l2_read, drv, 0); +#endif /* CONFIG_AP */ -failed: - netlink_deinit(drv->netlink); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); + if (drv->global) { + dl_list_add(&drv->global->interfaces, &drv->list); + drv->in_interface_list = 1; + } - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); + return bss; - os_free(drv); +failed: + wpa_driver_nl80211_deinit(bss); return NULL; } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, - const u8 *match, size_t match_len) +static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, + struct nl_handle *nl_handle, + u16 type, const u8 *match, size_t match_len) { struct nl_msg *msg; int ret = -1; @@ -1439,18 +2423,19 @@ static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_REGISTER_ACTION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); - ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); + ret = send_and_recv(drv, nl_handle, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Register Action command " - "failed: ret=%d (%s)", ret, strerror(-ret)); - wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match", + wpa_printf(MSG_DEBUG, "nl80211: Register frame command " + "failed (type=%u): ret=%d (%s)", + type, ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", match, match_len); goto nla_put_failure; } @@ -1461,8 +2446,57 @@ nla_put_failure: } +static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(drv, drv->nl_event.handle, + type, match, match_len); +} + + static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) { +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) + /* GAS Initial Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) + return -1; +#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + /* P2P Public Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_TDLS + if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { + /* TDLS Discovery Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) < + 0) + return -1; + } +#endif /* CONFIG_TDLS */ + /* FT Action frames */ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) return -1; @@ -1470,36 +2504,66 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + /* WNM - BSS Transition Management Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x0a\x07", 2) < 0) + return -1; + return 0; } +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { struct i802_bss *bss = &drv->first_bss; + int send_rfkill_event = 0; drv->ifindex = if_nametoindex(bss->ifname); drv->first_bss.ifindex = drv->ifindex; #ifndef HOSTAPD - if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to " + /* + * Make sure the interface starts up in station mode unless this is a + * dynamically added interface (e.g., P2P) that was already configured + * with proper iftype. + */ + if (drv->ifindex != drv->global->if_add_ifindex && + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to " "use managed mode"); + return -1; } - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_ERROR, "Could not set interface '%s' UP", - bss->ifname); - return -1; + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } } + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); +#endif /* HOSTAPD */ + if (wpa_driver_nl80211_capa(drv)) return -1; - netlink_send_oper_ifla(drv->netlink, drv->ifindex, - 1, IF_OPER_DORMANT); -#endif /* HOSTAPD */ + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + drv->addr)) + return -1; if (nl80211_register_action_frames(drv) < 0) { wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " @@ -1511,6 +2575,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) */ } + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); + } + return 0; } @@ -1523,12 +2592,12 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_BEACON, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -1545,23 +2614,30 @@ static void wpa_driver_nl80211_deinit(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->added_if_into_bridge) { - if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname) - < 0) +#ifdef CONFIG_AP + if (drv->l2) + l2_packet_deinit(drv->l2); +#endif /* CONFIG_AP */ + + if (drv->nl_preq.handle) + wpa_driver_nl80211_probe_req_report(bss, 0); + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", - bss->ifname, drv->brname, strerror(errno)); + bss->ifname, bss->brname, strerror(errno)); } - if (drv->added_bridge) { - if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "bridge %s: %s", - drv->brname, strerror(errno)); + bss->brname, strerror(errno)); } nl80211_remove_monitor_interface(drv); - if (drv->nlmode == NL80211_IFTYPE_AP) + if (is_ap_interface(drv->nlmode)) wpa_driver_nl80211_del_beacon(drv); #ifdef HOSTAPD @@ -1582,33 +2658,26 @@ static void wpa_driver_nl80211_deinit(void *priv) os_free(drv->if_indices); #endif /* HOSTAPD */ - if (drv->disable_11b_rates) + if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); - netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); - netlink_deinit(drv->netlink); + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, + IF_OPER_UP); + rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0); - wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA); - - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); + (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_cache_free(drv->nl_cache_event); - nl_handle_destroy(drv->nl_handle); - nl_handle_destroy(drv->nl_handle_event); - nl_cb_put(drv->nl_cb); - - eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle)); + nl_destroy_handles(&drv->nl_event); os_free(drv->filter_ssids); + if (drv->in_interface_list) + dl_list_del(&drv->list); + os_free(drv); } @@ -1624,10 +2693,10 @@ static void wpa_driver_nl80211_deinit(void *priv) static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->ap_scan_as_station) { + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { wpa_driver_nl80211_set_mode(&drv->first_bss, - IEEE80211_MODE_AP); - drv->ap_scan_as_station = 0; + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); @@ -1646,16 +2715,18 @@ static int wpa_driver_nl80211_scan(void *priv, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = 0, timeout; - struct nl_msg *msg, *ssids, *freqs; + struct nl_msg *msg, *ssids, *freqs, *rates; size_t i; msg = nlmsg_alloc(); ssids = nlmsg_alloc(); freqs = nlmsg_alloc(); - if (!msg || !ssids || !freqs) { + rates = nlmsg_alloc(); + if (!msg || !ssids || !freqs || !rates) { nlmsg_free(msg); nlmsg_free(ssids); nlmsg_free(freqs); + nlmsg_free(rates); return -1; } @@ -1664,8 +2735,7 @@ static int wpa_driver_nl80211_scan(void *priv, params->filter_ssids = NULL; drv->num_filter_ssids = params->num_filter_ssids; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_TRIGGER_SCAN, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); @@ -1695,29 +2765,42 @@ static int wpa_driver_nl80211_scan(void *priv, nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); } + if (params->p2p_probe) { + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + NLA_PUT(rates, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates); + + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); #ifdef HOSTAPD - if (drv->nlmode == NL80211_IFTYPE_AP) { + if (is_ap_interface(drv->nlmode)) { /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ - if (wpa_driver_nl80211_set_mode(bss, - IEEE80211_MODE_INFRA)) + if (wpa_driver_nl80211_set_mode( + bss, NL80211_IFTYPE_STATION)) goto nla_put_failure; if (wpa_driver_nl80211_scan(drv, params)) { - wpa_driver_nl80211_set_mode(bss, - IEEE80211_MODE_AP); + wpa_driver_nl80211_set_mode(bss, drv->nlmode); goto nla_put_failure; } /* Restore AP mode when processing scan results */ - drv->ap_scan_as_station = 1; + drv->ap_scan_as_station = drv->nlmode; ret = 0; } else goto nla_put_failure; @@ -1747,6 +2830,154 @@ nla_put_failure: nlmsg_free(ssids); nlmsg_free(msg); nlmsg_free(freqs); + nlmsg_free(rates); + return ret; +} + + +/** + * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets; + size_t i; + + msg = nlmsg_alloc(); + ssids = nlmsg_alloc(); + freqs = nlmsg_alloc(); + if (!msg || !ssids || !freqs) { + nlmsg_free(msg); + nlmsg_free(ssids); + nlmsg_free(freqs); + return -1; + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); + + if (drv->num_filter_ssids) { + match_sets = nlmsg_alloc(); + + for (i = 0; i < drv->num_filter_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID", + drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len); + + match_set_ssid = nlmsg_alloc(); + nla_put(match_set_ssid, + NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + drv->filter_ssids[i].ssid_len, + drv->filter_ssids[i].ssid); + + nla_put_nested(match_sets, i + 1, match_set_ssid); + + nlmsg_free(match_set_ssid); + } + + nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, + match_sets); + nlmsg_free(match_sets); + } + + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid); + } + if (params->num_ssids) + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + if (params->extra_ies) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs", + params->extra_ies, params->extra_ies_len); + NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies); + } + + if (params->freqs) { + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + } + nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + /* TODO: if we get an error here, we should fall back to normal scan */ + + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " + "scan interval %d msec", ret, interval); + +nla_put_failure: + nlmsg_free(ssids); + nlmsg_free(msg); + nlmsg_free(freqs); + return ret; +} + + +/** + * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_stop_sched_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); + +nla_put_failure: + nlmsg_free(msg); return ret; } @@ -1797,11 +3028,6 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, } -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; -}; - static int bss_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1827,6 +3053,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) const u8 *ie, *beacon_ie; size_t ie_len, beacon_ie_len; u8 *pos; + size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -1835,6 +3062,26 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) return NL_SKIP; + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->assoc_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + _arg->assoc_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(_arg->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(_arg->assoc_bssid)); + } + } + if (!res) + return NL_SKIP; if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); @@ -1873,7 +3120,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - r->flags |= WPA_SCAN_LEVEL_INVALID; + r->flags |= WPA_SCAN_QUAL_INVALID; } else r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) @@ -1905,6 +3152,38 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) } } + /* + * cfg80211 maintains separate BSS table entries for APs if the same + * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does + * not use frequency as a separate key in the BSS table, so filter out + * duplicated entries. Prefer associated BSS entry in such a case in + * order to get the correct frequency into the BSS table. + */ + for (i = 0; i < res->num; i++) { + const u8 *s1, *s2; + if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) + continue; + + s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || + os_memcmp(s1, s2, 2 + s1[1]) != 0) + continue; + + /* Same BSSID,SSID was already included in scan results */ + wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " + "for " MACSTR, MAC2STR(r->bssid)); + + if ((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) { + os_free(res->res[i]); + res->res[i] = r; + } else + os_free(r); + return NL_SKIP; + } + tmp = os_realloc(res->res, (res->num + 1) * sizeof(struct wpa_scan_res *)); if (tmp == NULL) { @@ -1943,7 +3222,7 @@ static void wpa_driver_nl80211_check_bss_status( "indicates BSS status with " MACSTR " as authenticated", MAC2STR(r->bssid)); - if (drv->nlmode == NL80211_IFTYPE_STATION && + if (is_sta_interface(drv->nlmode) && os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != 0) { @@ -1961,13 +3240,13 @@ static void wpa_driver_nl80211_check_bss_status( "indicate BSS status with " MACSTR " as associated", MAC2STR(r->bssid)); - if (drv->nlmode == NL80211_IFTYPE_STATION && + if (is_sta_interface(drv->nlmode) && !drv->associated) { wpa_printf(MSG_DEBUG, "nl80211: Local state " "(not associated) does not match " "with BSS state"); clear_state_mismatch(drv, r->bssid); - } else if (drv->nlmode == NL80211_IFTYPE_STATION && + } else if (is_sta_interface(drv->nlmode) && os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "nl80211: Local state " @@ -1982,20 +3261,6 @@ static void wpa_driver_nl80211_check_bss_status( } -static void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - static struct wpa_scan_results * nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) { @@ -2011,8 +3276,7 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) if (!msg) goto nla_put_failure; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, - NL80211_CMD_GET_SCAN, 0); + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); arg.drv = drv; @@ -2020,8 +3284,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { - wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", - (unsigned long) res->num); + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " @@ -2092,17 +3357,19 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, "set_tx=%d seq_len=%lu key_len=%lu", __func__, ifindex, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); +#ifdef CONFIG_TDLS + if (key_idx == -1) + key_idx = 0; +#endif /* CONFIG_TDLS */ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; if (alg == WPA_ALG_NONE) { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); } else { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); switch (alg) { case WPA_ALG_WEP: @@ -2136,10 +3403,28 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, if (seq && seq_len) NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); - if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) - { + if (addr && !is_broadcast_ether_addr(addr)) { wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + if (alg != WPA_ALG_WEP && key_idx && !set_tx) { + wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, + NL80211_KEYTYPE_GROUP); + } + } else if (addr && is_broadcast_ether_addr(addr)) { + struct nl_msg *types; + int err; + wpa_printf(MSG_DEBUG, " broadcast key"); + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; } NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); @@ -2157,26 +3442,46 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, */ if (ret || !set_tx || alg == WPA_ALG_NONE) return ret; -#ifdef HOSTAPD - if (addr) - return ret; -#else /* HOSTAPD */ - if (drv->nlmode == NL80211_IFTYPE_AP && addr) + if (is_ap_interface(drv->nlmode) && addr && + !is_broadcast_ether_addr(addr)) return ret; -#endif /* HOSTAPD */ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); if (alg == WPA_ALG_IGTK) NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); else NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + if (addr && is_broadcast_ether_addr(addr)) { + struct nl_msg *types; + int err; + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; + } else if (addr) { + struct nl_msg *types; + int err; + types = nlmsg_alloc(); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST); + err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, + types); + nlmsg_free(types); + if (err) + goto nla_put_failure; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == -ENOENT) @@ -2187,6 +3492,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -2257,6 +3563,12 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, privacy = 1; break; } + if (params->wps == WPS_MODE_PRIVACY) + privacy = 1; + if (params->pairwise_suite && + params->pairwise_suite != WPA_CIPHER_NONE) + privacy = 1; + if (!privacy) return 0; @@ -2310,7 +3622,7 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0); + nl80211_cmd(drv, msg, 0, cmd); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); @@ -2336,7 +3648,8 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, const u8 *addr, int reason_code) { - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, reason_code, 0); @@ -2350,8 +3663,11 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; + if (drv->nlmode == NL80211_IFTYPE_ADHOC) + return nl80211_leave_ibss(drv); return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, reason_code, 0); } @@ -2379,15 +3695,16 @@ static int wpa_driver_nl80211_authenticate( int ret = -1, i; struct nl_msg *msg; enum nl80211_auth_type type; + enum nl80211_iftype nlmode; int count = 0; drv->associated = 0; os_memset(drv->auth_bssid, 0, ETH_ALEN); /* FIX: IBSS mode */ - if (drv->nlmode != NL80211_IFTYPE_STATION) - wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); - - if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0) + nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + if (drv->nlmode != nlmode && + wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; retry: @@ -2398,8 +3715,7 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_AUTHENTICATE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); for (i = 0; i < 4; i++) { if (!params->wep_key[i]) @@ -2539,6 +3855,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode = &phy_info->modes[*(phy_info->num_modes)]; memset(mode, 0, sizeof(*mode)); + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN; *(phy_info->num_modes) += 1; nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), @@ -2737,6 +4054,153 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) } +static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40; + } +} + + +static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_HT40)) + continue; + if (chan->freq - 30 >= start && chan->freq - 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40MINUS; + if (chan->freq + 10 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_HT40PLUS; + } +} + + +static void nl80211_reg_rule_ht40(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz", + start, end, max_bw); + if (max_bw < 40) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 20) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + } +} + + +static int nl80211_get_reg(struct nl_msg *msg, void *arg) +{ + struct phy_info_arg *results = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *nl_rule; + struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; + int rem_rule; + static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + }; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || + !tb_msg[NL80211_ATTR_REG_RULES]) { + wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " + "available"); + return NL_SKIP; + } + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_ht40(tb_rule, results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_sec(tb_rule, results); + } + + return NL_SKIP; +} + + +static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); +} + + static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { @@ -2755,14 +4219,17 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) if (!msg) return NULL; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + nl80211_set_ht40_flags(drv, &result); return wpa_driver_nl80211_add_11b(result.modes, num_modes); + } + msg = NULL; nla_put_failure: + nlmsg_free(msg); return NULL; } @@ -2799,11 +4266,23 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, .msg_controllen = 0, .msg_flags = 0, }; + int res; if (encrypt) rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; - return sendmsg(drv->monitor_sock, &msg, 0); + if (drv->monitor_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " + "for %s", __func__); + return -1; + } + + res = sendmsg(drv->monitor_sock, &msg, 0); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + return -1; + } + return 0; } @@ -2819,6 +4298,24 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); + if (is_sta_interface(drv->nlmode) && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + /* + * The use of last_mgmt_freq is a bit of a hack, + * but it works due to the single-threaded nature + * of wpa_supplicant. + */ + return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0, + data, data_len, NULL, 1); + } + + if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) { + return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0, + data, data_len, + &drv->send_action_cookie, 0); + } + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { /* @@ -2837,10 +4334,57 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, } -static int wpa_driver_nl80211_set_beacon(void *priv, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, - int dtim_period, int beacon_int) +static int nl80211_set_ap_isolate(struct i802_bss *bss, int enabled) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, enabled); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, + int slot, int ht_opmode) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + if (ht_opmode >= 0) + NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_ap(void *priv, + struct wpa_driver_ap_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -2849,6 +4393,9 @@ static int wpa_driver_nl80211_set_beacon(void *priv, int ret; int beacon_set; int ifindex = if_nametoindex(bss->ifname); + int num_suites; + u32 suites[10]; + u32 ver; beacon_set = bss->beacon_set; @@ -2861,13 +4408,105 @@ static int wpa_driver_nl80211_set_beacon(void *priv, if (beacon_set) cmd = NL80211_CMD_SET_BEACON; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, cmd, 0); - NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); - NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); + nl80211_cmd(drv, msg, 0, cmd); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int); - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + switch (params->hide_ssid) { + case NO_SSID_HIDING: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_NOT_IN_USE); + break; + case HIDDEN_SSID_ZERO_LEN: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_LEN); + break; + case HIDDEN_SSID_ZERO_CONTENTS: + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_CONTENTS); + break; + } + if (params->privacy) + NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == + (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { + /* Leave out the attribute */ + } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_SHARED_KEY); + else + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_OPEN_SYSTEM); + + ver = 0; + if (params->wpa_version & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_version & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; + if (ver) + NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + + num_suites = 0; + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) + suites[num_suites++] = WLAN_AKM_SUITE_8021X; + if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) + suites[num_suites++] = WLAN_AKM_SUITE_PSK; + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, + num_suites * sizeof(u32), suites); + } + + num_suites = 0; + if (params->pairwise_ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (params->pairwise_ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (params->pairwise_ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (params->pairwise_ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + num_suites * sizeof(u32), suites); + } + + switch (params->group_cipher) { + case WPA_CIPHER_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_CIPHER_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_CIPHER_WEP104: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_CIPHER_WEP40: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP40); + break; + } + + if (params->beacon_ies) { + NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), + wpabuf_head(params->beacon_ies)); + } + if (params->proberesp_ies) { + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + wpabuf_len(params->proberesp_ies), + wpabuf_head(params->proberesp_ies)); + } + if (params->assocresp_ies) { + NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, + wpabuf_len(params->assocresp_ies), + wpabuf_head(params->assocresp_ies)); + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { @@ -2875,9 +4514,20 @@ static int wpa_driver_nl80211_set_beacon(void *priv, ret, strerror(-ret)); } else { bss->beacon_set = 1; + ret = nl80211_set_ap_isolate(bss, params->isolate); + if (!params->isolate && ret) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore AP isolation " + "configuration error since isolation is " + "not used"); + ret = 0; + } + + nl80211_set_bss(bss, params->cts_protect, params->preamble, + params->short_slot_time, params->ht_opmode); } return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -2889,12 +4539,14 @@ static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg; int ret; + wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d " + "sec_channel_offset=%d)", + freq, ht_enabled, sec_channel_offset); msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); @@ -2916,50 +4568,86 @@ static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, } ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " "%d (%s)", freq, ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } +static u32 sta_flags_nl80211(int flags) +{ + u32 f = 0; + + if (flags & WPA_STA_AUTHORIZED) + f |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (flags & WPA_STA_WMM) + f |= BIT(NL80211_STA_FLAG_WME); + if (flags & WPA_STA_SHORT_PREAMBLE) + f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (flags & WPA_STA_MFP) + f |= BIT(NL80211_STA_FLAG_MFP); + if (flags & WPA_STA_TDLS_PEER) + f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + return f; +} + + static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + struct nl80211_sta_flag_update upd; int ret = -ENOBUFS; + if ((params->flags & WPA_STA_TDLS_PEER) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_STATION, 0); + nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, - params->listen_interval); + if (!params->set) { + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + } if (params->ht_capabilities) { NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(*params->ht_capabilities), params->ht_capabilities); } + os_memset(&upd, 0, sizeof(upd)); + upd.mask = sta_flags_nl80211(params->flags); + upd.set = upd.mask; + NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) - wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " - "result: %d (%s)", ret, strerror(-ret)); + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " + "result: %d (%s)", params->set ? "SET" : "NEW", ret, + strerror(-ret)); if (ret == -EEXIST) ret = 0; nla_put_failure: + nlmsg_free(msg); return ret; } @@ -2975,8 +4663,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -2987,6 +4674,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) return 0; return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -2998,26 +4686,46 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); -#ifdef HOSTAPD /* stop listening for EAPOL on this interface */ del_ifidx(drv, ifidx); -#endif /* HOSTAPD */ msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return; + msg = NULL; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); } +static const char * nl80211_iftype_str(enum nl80211_iftype mode) +{ + switch (mode) { + case NL80211_IFTYPE_ADHOC: + return "ADHOC"; + case NL80211_IFTYPE_STATION: + return "STATION"; + case NL80211_IFTYPE_AP: + return "AP"; + case NL80211_IFTYPE_MONITOR: + return "MONITOR"; + case NL80211_IFTYPE_P2P_CLIENT: + return "P2P_CLIENT"; + case NL80211_IFTYPE_P2P_GO: + return "P2P_GO"; + default: + return "unknown"; + } +} + + static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, @@ -3027,12 +4735,14 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, int ifidx; int ret = -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", + iftype, nl80211_iftype_str(iftype)); + msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); @@ -3057,8 +4767,10 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, } ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) { nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", ifname, ret, strerror(-ret)); return ret; @@ -3071,13 +4783,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, if (ifidx <= 0) return -1; -#ifdef HOSTAPD /* start listening for EAPOL on this interface */ add_ifidx(drv, ifidx); -#endif /* HOSTAPD */ if (addr && iftype != NL80211_IFTYPE_MONITOR && - linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) { + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) { nl80211_remove_iface(drv, ifidx); return -1; } @@ -3094,7 +4804,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); - /* if error occured and interface exists already */ + /* if error occurred and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); @@ -3106,7 +4816,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, wds); } - if (ret >= 0 && drv->disable_11b_rates) + if (ret >= 0 && is_p2p_interface(iftype)) nl80211_disable_11b_rates(drv, ret, 1); return ret; @@ -3136,10 +4846,20 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, u8 *buf, size_t len) { + struct ieee80211_hdr *hdr = (void *)buf; + u16 fc; union wpa_event_data event; + + if (len < sizeof(*hdr)) + return; + + fc = le_to_host16(hdr->frame_control); + os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.frame = buf; - event.rx_from_unknown.len = len; + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = hdr->addr2; + event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == + (WLAN_FC_FROMDS | WLAN_FC_TODS); wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); } @@ -3191,12 +4911,6 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface " - "frame since Probe Request reporting is disabled"); - return; - } - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { printf("received invalid radiotap frame\n"); return; @@ -3290,8 +5004,15 @@ static struct sock_filter msock_filter_insns[] = { * add a filter here that filters on our DA and that flag * to allow us to deauth frames to that bad station. * - * Not a regression -- we didn't do it before either. + * For now allow all To DS data frames through. */ + /* load the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), + /* mask off frame type, version and DS status */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), + /* accept frame if version 0, type 2 and To DS, fall through otherwise + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), #if 0 /* @@ -3416,17 +5137,35 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) int optval; socklen_t optlen; - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) { + /* + * P2P interface name is of the format p2p-%s-%d. For monitor + * interface name corresponding to P2P GO, replace "p2p-" with + * "mon-" to retain the same interface name length and to + * indicate that it is a monitor interface. + */ + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4); + } else { + /* Non-P2P interface with AP functionality. */ + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + } + buf[IFNAMSIZ - 1] = '\0'; drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, 0); + if (drv->monitor_ifidx == -EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " + "monitor interface type - try to run without it"); + drv->no_monitor_iface_capab = 1; + } + if (drv->monitor_ifidx < 0) return -1; - if (linux_set_iface_flags(drv->ioctl_sock, buf, 1)) + if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) goto error; memset(&ll, 0, sizeof(ll)); @@ -3470,11 +5209,29 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) } +#ifdef CONFIG_AP +static int nl80211_send_eapol_data(struct i802_bss *bss, + const u8 *addr, const u8 *data, + size_t data_len, const u8 *own_addr) +{ + if (bss->drv->l2 == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL"); + return -1; + } + + if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) < + 0) + return -1; + return 0; +} +#endif /* CONFIG_AP */ + + static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; static int wpa_driver_nl80211_hapd_send_eapol( void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr) + size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -3482,11 +5239,13 @@ static int wpa_driver_nl80211_hapd_send_eapol( size_t len; u8 *pos; int res; -#if 0 /* FIX */ - int qos = sta->flags & WPA_STA_WMM; -#else - int qos = 0; -#endif + int qos = flags & WPA_STA_WMM; + +#ifdef CONFIG_AP + if (drv->no_monitor_iface_capab) + return nl80211_send_eapol_data(bss, addr, data, data_len, + own_addr); +#endif /* CONFIG_AP */ len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + data_len; @@ -3502,26 +5261,22 @@ static int wpa_driver_nl80211_hapd_send_eapol( hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); if (encrypt) hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); -#if 0 /* To be enabled if qos determination is added above */ if (qos) { hdr->frame_control |= host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); } -#endif memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); pos = (u8 *) (hdr + 1); -#if 0 /* To be enabled if qos determination is added above */ if (qos) { /* add an empty QoS header if needed */ pos[0] = 0; pos[1] = 0; pos += 2; } -#endif memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); pos += sizeof(rfc1042_header); @@ -3535,26 +5290,9 @@ static int wpa_driver_nl80211_hapd_send_eapol( "failed: %d (%s)", (unsigned long) len, errno, strerror(errno)); } - os_free(hdr); - - return res; -} - - -static u32 sta_flags_nl80211(int flags) -{ - u32 f = 0; - - if (flags & WPA_STA_AUTHORIZED) - f |= BIT(NL80211_STA_FLAG_AUTHORIZED); - if (flags & WPA_STA_WMM) - f |= BIT(NL80211_STA_FLAG_WME); - if (flags & WPA_STA_SHORT_PREAMBLE) - f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (flags & WPA_STA_MFP) - f |= BIT(NL80211_STA_FLAG_MFP); + os_free(hdr); - return f; + return res; } @@ -3577,8 +5315,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, return -ENOMEM; } - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -3600,6 +5337,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, if (total_flags & WPA_STA_MFP) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + if (total_flags & WPA_STA_TDLS_PEER) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER); + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) goto nla_put_failure; @@ -3612,6 +5352,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); nlmsg_free(flags); return -ENOBUFS; } @@ -3620,14 +5361,31 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { - if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || + enum nl80211_iftype nlmode; + + if (params->p2p) { + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); + nlmode = NL80211_IFTYPE_P2P_GO; + } else + nlmode = NL80211_IFTYPE_AP; + + if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) || wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { nl80211_remove_monitor_interface(drv); return -1; } - /* TODO: setup monitor interface (and add code somewhere to remove this - * when AP mode is stopped; associate with mode != 2 or drv_deinit) */ + if (drv->no_monitor_iface_capab) { + if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0) + { + wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " + "Probe Request frame reporting in AP mode"); + /* Try to survive without this */ + } + } + + drv->ap_oper_freq = params->freq; return 0; } @@ -3642,8 +5400,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_LEAVE_IBSS, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -3671,7 +5428,8 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) { + if (wpa_driver_nl80211_set_mode(&drv->first_bss, + NL80211_IFTYPE_ADHOC)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; @@ -3682,8 +5440,7 @@ retry: if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_JOIN_IBSS, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) @@ -3736,6 +5493,55 @@ nla_put_failure: } +static unsigned int nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv, + u8 *bssid) +{ + struct nl_msg *msg; + int ret; + struct nl80211_bss_info_arg arg; + + os_memset(&arg, 0, sizeof(arg)); + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + msg = NULL; + if (ret == 0) { + if (is_zero_ether_addr(arg.assoc_bssid)) + return -ENOTCONN; + os_memcpy(bssid, arg.assoc_bssid, ETH_ALEN); + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return drv->assoc_freq; +} + + +static int nl80211_disconnect(struct wpa_driver_nl80211_data *drv, + const u8 *bssid) +{ + u8 addr[ETH_ALEN]; + + if (bssid == NULL) { + int res = nl80211_get_assoc_bssid(drv, addr); + if (res) + return res; + bssid = addr; + } + + return wpa_driver_nl80211_disconnect(drv, bssid, + WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + static int wpa_driver_nl80211_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) @@ -3743,14 +5549,14 @@ static int wpa_driver_nl80211_connect( struct nl_msg *msg; enum nl80211_auth_type type; int ret = 0; + int algs; msg = nlmsg_alloc(); if (!msg) return -1; wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_CONNECT, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { @@ -3777,6 +5583,19 @@ static int wpa_driver_nl80211_connect( NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); + algs = 0; + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_LEAP) + algs++; + if (algs > 1) { + wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " + "selection"); + goto skip_auth_type; + } + if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -3791,15 +5610,16 @@ static int wpa_driver_nl80211_connect( wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); - if (params->wpa_ie && params->wpa_ie_len) { - enum nl80211_wpa_versions ver; +skip_auth_type: + if (params->wpa_proto) { + enum nl80211_wpa_versions ver = 0; - if (params->wpa_ie[0] == WLAN_EID_RSN) - ver = NL80211_WPA_VERSION_2; - else - ver = NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; - wpa_printf(MSG_DEBUG, " * WPA Version %d", ver); + wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); } @@ -3870,6 +5690,14 @@ static int wpa_driver_nl80211_connect( if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " "(%s)", ret, strerror(-ret)); + /* + * cfg80211 does not currently accept new connection if we are + * already connected. As a workaround, force disconnection and + * try again once the driver indicates it completed + * disconnection. + */ + if (ret == -EALREADY) + nl80211_disconnect(drv, params->bssid); goto nla_put_failure; } ret = 0; @@ -3897,7 +5725,10 @@ static int wpa_driver_nl80211_associate( return wpa_driver_nl80211_ibss(drv, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0) + enum nl80211_iftype nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; return wpa_driver_nl80211_connect(drv, params); } @@ -3910,8 +5741,7 @@ static int wpa_driver_nl80211_associate( wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ASSOCIATE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { @@ -3940,6 +5770,50 @@ static int wpa_driver_nl80211_associate( NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); + if (params->pairwise_suite != CIPHER_NONE) { + int cipher; + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != CIPHER_NONE) { + int cipher; + + switch (params->group_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + #ifdef CONFIG_IEEE80211W if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); @@ -3954,6 +5828,9 @@ static int wpa_driver_nl80211_associate( params->prev_bssid); } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -3973,50 +5850,41 @@ nla_put_failure: static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, - int ifindex, int mode) + int ifindex, enum nl80211_iftype mode) { struct nl_msg *msg; int ret = -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", + ifindex, mode, nl80211_iftype_str(mode)); + msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_INTERFACE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" " %d (%s)", ifindex, mode, ret, strerror(-ret)); return ret; } -static int wpa_driver_nl80211_set_mode(void *priv, int mode) +static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; - int nlmode; - - switch (mode) { - case 0: - nlmode = NL80211_IFTYPE_STATION; - break; - case 1: - nlmode = NL80211_IFTYPE_ADHOC; - break; - case 2: - nlmode = NL80211_IFTYPE_AP; - break; - default: - return -1; - } + int i; + int was_ap = is_ap_interface(drv->nlmode); if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) { drv->nlmode = nlmode; @@ -4035,31 +5903,62 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) * take the device down, try to set the mode again, and bring the * device back up. */ - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) { - /* Try to set the mode again while the interface is down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) - ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + int res; + res = linux_set_iface_flags(drv->global->ioctl_sock, + bss->ifname, 0); + if (res == -EACCES || res == -ENODEV) + break; + if (res == 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (ret == -EACCES) + break; + res = linux_set_iface_flags(drv->global->ioctl_sock, + bss->ifname, 1); + if (res && !ret) + ret = -1; + else if (ret != -EBUSY) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); } if (!ret) { wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " "interface is down"); drv->nlmode = nlmode; + drv->ignore_if_down_event = 1; } done: - if (!ret && nlmode == NL80211_IFTYPE_AP) { + if (!ret && is_ap_interface(nlmode)) { /* Setup additional AP mode functionality if needed */ - if (drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv)) + if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 && + nl80211_create_monitor_interface(drv) && + !drv->no_monitor_iface_capab) return -1; - } else if (!ret && nlmode != NL80211_IFTYPE_AP) { + } else if (!ret && !is_ap_interface(nlmode)) { /* Remove additional AP mode functionality */ + if (was_ap && drv->no_monitor_iface_capab) + wpa_driver_nl80211_probe_req_report(bss, 0); nl80211_remove_monitor_interface(drv); bss->beacon_set = 0; } + if (!ret && is_p2p_interface(drv->nlmode)) { + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + drv->disabled_11b_rates = 1; + } else if (!ret && drv->disabled_11b_rates) { + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + drv->disabled_11b_rates = 0; + } + if (ret) wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " "from %d failed", nlmode, drv->nlmode); @@ -4088,7 +5987,7 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state) wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; - return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); } @@ -4104,8 +6003,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4119,74 +6017,22 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -#ifdef HOSTAPD - -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ - int i; - int *old; - - wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", - ifidx); - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == 0) { - drv->if_indices[i] = ifidx; - return; - } - } - - if (drv->if_indices != drv->default_if_indices) - old = drv->if_indices; - else - old = NULL; - - drv->if_indices = os_realloc(old, - sizeof(int) * (drv->num_if_indices + 1)); - if (!drv->if_indices) { - if (!old) - drv->if_indices = drv->default_if_indices; - else - drv->if_indices = old; - wpa_printf(MSG_ERROR, "Failed to reallocate memory for " - "interfaces"); - wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); - return; - } else if (!old) - os_memcpy(drv->if_indices, drv->default_if_indices, - sizeof(drv->default_if_indices)); - drv->if_indices[drv->num_if_indices] = ifidx; - drv->num_if_indices++; -} - - -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { - int i; - - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == ifidx) { - drv->if_indices[i] = 0; - break; - } - } + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled, + freq->sec_channel_offset); } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ - int i; - - for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) - return 1; - - return 0; -} - +#if defined(HOSTAPD) || defined(CONFIG_AP) static inline int min_int(int a, int b) { @@ -4228,8 +6074,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_KEY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); if (addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); @@ -4240,6 +6085,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, return send_and_recv_msgs(drv, msg, get_key_handler, seq); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -4258,8 +6104,7 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_BSS, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) rates[rates_len++] = basic_rates[i] / 5; @@ -4273,20 +6118,6 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, return -ENOBUFS; } -#endif /* HOSTAPD */ - - -/* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled, - freq->sec_channel_offset); -} - - -#ifdef HOSTAPD static int i802_set_rts(void *priv, int rts) { @@ -4305,15 +6136,16 @@ static int i802_set_rts(void *priv, int rts) else val = rts; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " "%d (%s)", rts, ret, strerror(-ret)); return ret; @@ -4337,15 +6169,16 @@ static int i802_set_frag(void *priv, int frag) else val = frag; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (!ret) return 0; nla_put_failure: + nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " "%d: %d (%s)", frag, ret, strerror(-ret)); return ret; @@ -4362,8 +6195,7 @@ static int i802_flush(void *priv) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); /* * XXX: FIX! this needs to flush all VLANs too @@ -4373,6 +6205,7 @@ static int i802_flush(void *priv) return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -4440,14 +6273,14 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); return send_and_recv_msgs(drv, msg, get_sta_handler, data); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } @@ -4464,8 +6297,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_WIPHY, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4478,68 +6310,37 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (!params) goto nla_put_failure; - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + switch (queue) { + case 0: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); + break; + case 1: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); + break; + case 2: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); + break; + case 3: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); + break; + } /* Burst time is configured in units of 0.1 msec and TXOP parameter in * 32 usec, so need to convert the value here. */ NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); - - nla_nest_end(msg, params); - - nla_nest_end(msg, txq); - - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; - nla_put_failure: - return -1; -} - - -static int i802_set_bss(void *priv, int cts, int preamble, int slot) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_BSS, 0); - - if (cts >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); - if (preamble >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); - if (slot >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_cts_protect(void *priv, int value) -{ - return i802_set_bss(priv, value, -1, -1); -} - + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); -static int i802_set_preamble(void *priv, int value) -{ - return i802_set_bss(priv, -1, value, -1); -} + nla_nest_end(msg, params); + nla_nest_end(msg, txq); -static int i802_set_short_slot_time(void *priv, int value) -{ - return i802_set_bss(priv, -1, -1, value); + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + msg = NULL; + nla_put_failure: + nlmsg_free(msg); + return -1; } @@ -4555,8 +6356,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_STATION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); @@ -4565,6 +6365,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, if_nametoindex(ifname)); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", @@ -4572,53 +6373,11 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, strerror(-ret)); } nla_put_failure: + nlmsg_free(msg); return ret; } -static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - char name[IFNAMSIZ + 1]; - - os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); - wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR - " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); - if (val) { - if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, - NULL, 1) < 0) - return -1; - linux_set_iface_flags(drv->ioctl_sock, name, 1); - return i802_set_sta_vlan(priv, addr, name, 0); - } else { - i802_set_sta_vlan(priv, addr, bss->ifname, 0); - return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, - name); - } -} - - -static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - struct sockaddr_ll lladdr; - unsigned char buf[3000]; - int len; - socklen_t fromlen = sizeof(lladdr); - - len = recvfrom(sock, buf, sizeof(buf), 0, - (struct sockaddr *)&lladdr, &fromlen); - if (len < 0) { - perror("recv"); - return; - } - - if (have_ifidx(drv, lladdr.sll_ifindex)) - drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); -} - - static int i802_get_inact_sec(void *priv, const u8 *addr) { struct hostap_sta_driver_data data; @@ -4678,27 +6437,145 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, sizeof(mgmt.u.disassoc)); } +#endif /* HOSTAPD || CONFIG_AP */ + +#ifdef HOSTAPD + +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + int *old; + + wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", + ifidx); + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + return; + } + } + + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = os_realloc(old, + sizeof(int) * (drv->num_if_indices + 1)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } else if (!old) + os_memcpy(drv->if_indices, drv->default_if_indices, + sizeof(drv->default_if_indices)); + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; +} + + +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } + } +} + + +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; + + return 0; +} + + +static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char name[IFNAMSIZ + 1]; + + os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR + " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); + if (val) { + if (!if_nametoindex(name)) { + if (nl80211_create_iface(drv, name, + NL80211_IFTYPE_AP_VLAN, + NULL, 1) < 0) + return -1; + if (bridge_ifname && + linux_br_add_if(drv->global->ioctl_sock, + bridge_ifname, name) < 0) + return -1; + } + linux_set_iface_flags(drv->global->ioctl_sock, name, 1); + return i802_set_sta_vlan(priv, addr, name, 0); + } else { + i802_set_sta_vlan(priv, addr, bss->ifname, 0); + return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, + name); + } +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); + + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + perror("recv"); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); +} + static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, const char *brname, const char *ifname) { int ifindex; char in_br[IFNAMSIZ]; - os_strlcpy(drv->brname, brname, IFNAMSIZ); + os_strlcpy(bss->brname, brname, IFNAMSIZ); ifindex = if_nametoindex(brname); if (ifindex == 0) { /* * Bridge was configured, but the bridge device does * not exist. Try to add it now. */ - if (linux_br_add(drv->ioctl_sock, brname) < 0) { + if (linux_br_add(drv->global->ioctl_sock, brname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the " "bridge interface %s: %s", brname, strerror(errno)); return -1; } - drv->added_bridge = 1; + bss->added_bridge = 1; add_ifidx(drv, if_nametoindex(brname)); } @@ -4708,7 +6585,8 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " "bridge %s", ifname, in_br); - if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) { + if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) < + 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to " "remove interface %s from bridge " "%s: %s", @@ -4719,13 +6597,13 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", ifname, brname); - if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) { + if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " "into bridge %s: %s", ifname, brname, strerror(errno)); return -1; } - drv->added_if_into_bridge = 1; + bss->added_if_into_bridge = 1; return 0; } @@ -4741,11 +6619,15 @@ static void *i802_init(struct hostapd_data *hapd, int ifindex, br_ifindex; int br_added = 0; - bss = wpa_driver_nl80211_init(hapd, params->ifname); + bss = wpa_driver_nl80211_init(hapd, params->ifname, + params->global_priv); if (bss == NULL) return NULL; drv = bss->drv; + drv->nlmode = NL80211_IFTYPE_AP; + drv->eapol_sock = -1; + if (linux_br_get(brname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", params->ifname, brname); @@ -4773,26 +6655,26 @@ static void *i802_init(struct hostapd_data *hapd, /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0)) + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0)) goto failed; if (params->bssid) { - if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname, + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->bssid)) goto failed; } - if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) { + if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " "into AP mode", bss->ifname); goto failed; } if (params->num_bridge && params->bridge[0] && - i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) + i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) goto failed; drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); @@ -4807,22 +6689,14 @@ static void *i802_init(struct hostapd_data *hapd, goto failed; } - if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr)) + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + params->own_addr)) goto failed; return bss; failed: - nl80211_remove_monitor_interface(drv); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); - - os_free(drv); + wpa_driver_nl80211_deinit(bss); return NULL; } @@ -4841,19 +6715,66 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( switch (type) { case WPA_IF_STATION: return NL80211_IFTYPE_STATION; + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: + return NL80211_IFTYPE_P2P_CLIENT; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: return NL80211_IFTYPE_AP; + case WPA_IF_P2P_GO: + return NL80211_IFTYPE_P2P_GO; } return -1; } +#ifdef CONFIG_P2P + +static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0) + return 1; + } + return 0; +} + + +static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, + u8 *new_addr) +{ + unsigned int idx; + + if (!drv->global) + return -1; + + os_memcpy(new_addr, drv->addr, ETH_ALEN); + for (idx = 0; idx < 64; idx++) { + new_addr[0] = drv->addr[0] | 0x02; + new_addr[0] ^= idx << 2; + if (!nl80211_addr_in_use(drv->global, new_addr)) + break; + } + if (idx == 64) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " + MACSTR, MAC2STR(new_addr)); + + return 0; +} + +#endif /* CONFIG_P2P */ + + static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, - char *force_ifname, u8 *if_addr) + char *force_ifname, u8 *if_addr, + const char *bridge) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -4881,12 +6802,56 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (!addr && - linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) + linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + if_addr) < 0) { + nl80211_remove_iface(drv, ifidx); return -1; + } + +#ifdef CONFIG_P2P + if (!addr && + (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || + type == WPA_IF_P2P_GO)) { + /* Enforce unique P2P Interface Address */ + u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN]; + + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + own_addr) < 0 || + linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " + "for P2P group interface"); + if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + } + os_memcpy(if_addr, new_addr, ETH_ALEN); + } +#endif /* CONFIG_P2P */ #ifdef HOSTAPD + if (bridge && + i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " + "interface %s to a bridge %s", ifname, bridge); + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + if (type == WPA_IF_AP_BSS) { - if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { + if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) + { nl80211_remove_iface(drv, ifidx); os_free(new_bss); return -1; @@ -4901,6 +6866,9 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } #endif /* HOSTAPD */ + if (drv->global) + drv->global->if_add_ifindex = ifidx; + return 0; } @@ -4917,6 +6885,23 @@ static int wpa_driver_nl80211_if_remove(void *priv, __func__, type, ifname, ifindex); if (ifindex <= 0) return -1; + +#ifdef HOSTAPD + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); + } +#endif /* HOSTAPD */ + nl80211_remove_iface(drv, ifindex); #ifdef HOSTAPD @@ -4924,16 +6909,19 @@ static int wpa_driver_nl80211_if_remove(void *priv, return 0; if (bss != &drv->first_bss) { - struct i802_bss *tbss = &drv->first_bss; - - while (tbss) { - if (tbss->next != bss) - continue; + struct i802_bss *tbss; - tbss->next = bss->next; - os_free(bss); - break; + for (tbss = &drv->first_bss; tbss; tbss = tbss->next) { + if (tbss->next == bss) { + tbss->next = bss->next; + os_free(bss); + bss = NULL; + break; + } } + if (bss) + wpa_printf(MSG_INFO, "nl80211: %s - could not find " + "BSS %p in the list", __func__, bss); } #endif /* HOSTAPD */ @@ -4954,21 +6942,67 @@ static int cookie_handler(struct nl_msg *msg, void *arg) } +static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, + u64 *cookie_out, int no_cck) +{ + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (wait) + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + if (no_cck) + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s) (freq=%u wait=%u)", ret, strerror(-ret), + freq, wait); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; " + "cookie 0x%llx", (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, + int no_cck) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; - struct nl_msg *msg; u8 *buf; struct ieee80211_hdr *hdr; - u64 cookie; - wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)", - drv->ifindex); + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " + "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck); buf = os_zalloc(24 + data_len); if (buf == NULL) @@ -4981,44 +7015,43 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); - if (drv->nlmode == NL80211_IFTYPE_AP) { - ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); - os_free(buf); - return ret; - } + if (is_ap_interface(drv->nlmode)) + ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); + else + ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf, + 24 + data_len, + &drv->send_action_cookie, + no_cck); + + os_free(buf); + return ret; +} + + +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; msg = nlmsg_alloc(); - if (!msg) { - os_free(buf); - return -1; - } + if (!msg) + return; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ACTION, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf); - os_free(buf); - buf = NULL; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d " + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " "(%s)", ret, strerror(-ret)); - goto nla_put_failure; - } - wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " - "cookie 0x%llx", (long long unsigned int) cookie); - drv->send_action_cookie = cookie; - ret = 0; -nla_put_failure: - os_free(buf); + nla_put_failure: nlmsg_free(msg); - return ret; } @@ -5035,8 +7068,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_REMAIN_ON_CHANNEL, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); @@ -5044,16 +7076,20 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " "0x%llx for freq=%u MHz duration=%u", (long long unsigned int) cookie, freq, duration); drv->remain_on_chan_cookie = cookie; + drv->pending_remain_on_chan = 1; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } @@ -5079,76 +7115,72 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " "%d (%s)", ret, strerror(-ret)); nla_put_failure: + nlmsg_free(msg); return -1; } -static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, - void *timeout_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->monitor_ifidx < 0) - return; /* monitor interface already removed */ - - if (drv->nlmode != NL80211_IFTYPE_STATION) - return; /* not in station mode anymore */ - - if (drv->probe_req_report) - return; /* reporting enabled */ - - wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no " - "Probe Request reporting needed anymore"); - nl80211_remove_monitor_interface(drv); -} - - static int wpa_driver_nl80211_probe_req_report(void *priv, int report) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_STATION) { - wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only " - "allowed in station mode (iftype=%d)", - drv->nlmode); - return -1; + if (!report) { + if (drv->nl_preq.handle && drv->no_monitor_iface_capab && + is_ap_interface(drv->nlmode)) { + /* + * Do not disable Probe Request reporting that was + * enabled in nl80211_setup_ap(). + */ + wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " + "Probe Request reporting nl_preq=%p while " + "in AP mode", drv->nl_preq.handle); + } else if (drv->nl_preq.handle) { + wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " + "reporting nl_preq=%p", drv->nl_preq.handle); + eloop_unregister_read_sock( + nl_socket_get_fd(drv->nl_preq.handle)); + nl_destroy_handles(&drv->nl_preq); + } + return 0; } - drv->probe_req_report = report; - if (report) { - eloop_cancel_timeout( - wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); - if (drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv)) - return -1; - } else { - /* - * It takes a while to remove the monitor interface, so try to - * avoid doing this if it is needed again shortly. Instead, - * schedule the interface to be removed later if no need for it - * is seen. - */ - wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface " - "to be removed after 10 seconds of no use"); - eloop_register_timeout( - 10, 0, wpa_driver_nl80211_probe_req_report_timeout, - drv, NULL); + if (drv->nl_preq.handle) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on!"); + return 0; } + if (nl_create_handles(&drv->nl_preq, drv->global->nl_cb, "preq")) + return -1; + + if (nl80211_register_frame(drv, drv->nl_preq.handle, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) + goto out_err; + + eloop_register_read_sock(nl_socket_get_fd(drv->nl_preq.handle), + wpa_driver_nl80211_event_receive, drv, + drv->nl_preq.handle); + return 0; + + out_err: + nl_destroy_handles(&drv->nl_preq); + return -1; } @@ -5163,8 +7195,7 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_TX_BITRATE_MASK, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); @@ -5179,8 +7210,10 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, band = nla_nest_start(msg, NL80211_BAND_2GHZ); if (!band) goto nla_put_failure; - NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + if (disabled) { + NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + } nla_nest_end(msg, band); nla_nest_end(msg, bands); @@ -5200,23 +7233,24 @@ nla_put_failure: } -static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) +static int wpa_driver_nl80211_deinit_ap(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - drv->disable_11b_rates = disabled; - return nl80211_disable_11b_rates(drv, drv->ifindex, disabled); + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } -static int wpa_driver_nl80211_deinit_ap(void *priv) +static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_AP) + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) return -1; - wpa_driver_nl80211_del_beacon(drv); - return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } @@ -5224,7 +7258,7 @@ static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " "resume event"); } @@ -5241,7 +7275,8 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, size_t data_len; u8 own_addr[ETH_ALEN]; - if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0) + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + own_addr) < 0) return -1; if (action != 1) { @@ -5272,9 +7307,9 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, pos += ETH_ALEN; os_memcpy(pos, ies, ies_len); - ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid, - own_addr, drv->bssid, - data, data_len); + ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, + drv->bssid, own_addr, drv->bssid, + data, data_len, 0); os_free(data); return ret; @@ -5294,8 +7329,7 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_CQM, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); @@ -5312,13 +7346,27 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) msg = NULL; nla_put_failure: - if (cqm) - nlmsg_free(cqm); + nlmsg_free(cqm); nlmsg_free(msg); return -1; } +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; + + return nl80211_get_link_noise(drv, si); +} + + static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, int encrypt) { @@ -5328,6 +7376,315 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, } +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; + +#ifdef CONFIG_P2P + if (os_strstr(param, "use_p2p_group_interface=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } +#endif /* CONFIG_P2P */ + + return 0; +} + + +static void * nl80211_global_init(void) +{ + struct nl80211_global *global; + struct netlink_config *cfg; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + global->ioctl_sock = -1; + dl_list_init(&global->interfaces); + global->if_add_ifindex = -1; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err; + + cfg->ctx = global; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + global->netlink = netlink_init(cfg); + if (global->netlink == NULL) { + os_free(cfg); + goto err; + } + + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; + + global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + goto err; + } + + return global; + +err: + nl80211_global_deinit(global); + return NULL; +} + + +static void nl80211_global_deinit(void *priv) +{ + struct nl80211_global *global = priv; + if (global == NULL) + return; + if (!dl_list_empty(&global->interfaces)) { + wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " + "nl80211_global_deinit", + dl_list_len(&global->interfaces)); + } + + if (global->netlink) + netlink_deinit(global->netlink); + + if (global->nl80211) + genl_family_put(global->nl80211); + nl_destroy_handles(&global->nl); + + if (global->nl_cb) + nl_cb_put(global->nl_cb); + + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); + + os_free(global); +} + + +static const char * nl80211_get_radio_name(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; +} + + +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(bss->drv, msg, 0, cmd); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (pmkid) + NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); +} + + +static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); +} + + +static int nl80211_flush_pmkid(void *priv) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); + return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); +} + + +static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!replay_nested) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); + NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr); + + nla_nest_end(msg, replay_nested); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); +} + + +static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, + int qos) +{ + struct i802_bss *bss = priv; + struct { + struct ieee80211_hdr hdr; + u16 qos_ctl; + } STRUCT_PACKED nulldata; + size_t size; + + /* Send data frame to poll STA and check whether this frame is ACKed */ + + os_memset(&nulldata, 0, sizeof(nulldata)); + + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); + } else { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); + } + + nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0) + wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " + "send poll frame"); +} + + +#ifdef CONFIG_TDLS + +static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + if (!dst) + return -EINVAL; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); + NLA_PUT(msg, NL80211_ATTR_IE, len, buf); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + enum nl80211_tdls_operation nl80211_oper; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + switch (oper) { + case TDLS_DISCOVERY_REQ: + nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; + break; + case TDLS_SETUP: + nl80211_oper = NL80211_TDLS_SETUP; + break; + case TDLS_TEARDOWN: + nl80211_oper = NL80211_TDLS_TEARDOWN; + break; + case TDLS_ENABLE_LINK: + nl80211_oper = NL80211_TDLS_ENABLE_LINK; + break; + case TDLS_DISABLE_LINK: + nl80211_oper = NL80211_TDLS_DISABLE_LINK; + break; + case TDLS_ENABLE: + return 0; + case TDLS_DISABLE: + return 0; + default: + return -EINVAL; + } + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +#endif /* CONFIG TDLS */ + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -5335,18 +7692,22 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ssid = wpa_driver_nl80211_get_ssid, .set_key = wpa_driver_nl80211_set_key, .scan2 = wpa_driver_nl80211_scan, + .sched_scan = wpa_driver_nl80211_sched_scan, + .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, .deauthenticate = wpa_driver_nl80211_deauthenticate, .disassociate = wpa_driver_nl80211_disassociate, .authenticate = wpa_driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, - .init = wpa_driver_nl80211_init, + .global_init = nl80211_global_init, + .global_deinit = nl80211_global_deinit, + .init2 = wpa_driver_nl80211_init, .deinit = wpa_driver_nl80211_deinit, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, - .set_beacon = wpa_driver_nl80211_set_beacon, + .set_ap = wpa_driver_nl80211_set_ap, .if_add = wpa_driver_nl80211_if_add, .if_remove = wpa_driver_nl80211_if_remove, .send_mlme = wpa_driver_nl80211_send_mlme, @@ -5358,33 +7719,45 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #ifdef HOSTAPD .hapd_init = i802_init, .hapd_deinit = i802_deinit, + .set_wds_sta = i802_set_wds_sta, +#endif /* HOSTAPD */ +#if defined(HOSTAPD) || defined(CONFIG_AP) .get_seqnum = i802_get_seqnum, .flush = i802_flush, .read_sta_data = i802_read_sta_data, - .sta_deauth = i802_sta_deauth, - .sta_disassoc = i802_sta_disassoc, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, .set_rts = i802_set_rts, .set_frag = i802_set_frag, - .set_rate_sets = i802_set_rate_sets, - .set_cts_protect = i802_set_cts_protect, - .set_preamble = i802_set_preamble, - .set_short_slot_time = i802_set_short_slot_time, .set_tx_queue_params = i802_set_tx_queue_params, .set_sta_vlan = i802_set_sta_vlan, - .set_wds_sta = i802_set_wds_sta, -#endif /* HOSTAPD */ + .set_rate_sets = i802_set_rate_sets, + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, +#endif /* HOSTAPD || CONFIG_AP */ .set_freq = i802_set_freq, .send_action = wpa_driver_nl80211_send_action, + .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, .probe_req_report = wpa_driver_nl80211_probe_req_report, - .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates, .deinit_ap = wpa_driver_nl80211_deinit_ap, + .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, + .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, + .set_param = nl80211_set_param, + .get_radio_name = nl80211_get_radio_name, + .add_pmkid = nl80211_add_pmkid, + .remove_pmkid = nl80211_remove_pmkid, + .flush_pmkid = nl80211_flush_pmkid, + .set_rekey_info = nl80211_set_rekey_info, + .poll_client = nl80211_poll_client, +#ifdef CONFIG_TDLS + .send_tdls_mgmt = nl80211_send_tdls_mgmt, + .tdls_oper = nl80211_tdls_oper, +#endif /* CONFIG_TDLS */ }; diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c index 09d1ef5..a1e27be 100644 --- a/src/drivers/driver_ralink.c +++ b/src/drivers/driver_ralink.c @@ -1178,8 +1178,7 @@ static int wpa_driver_ralink_set_key(const char *ifname, void *priv, drv->bAddWepKey = FALSE; - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { /* Group Key */ pairwise = 0; wpa_driver_ralink_get_bssid(drv, bssid); diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 6877eda..61b75b1 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -14,10 +14,10 @@ #include "includes.h" #include -#include #include #include #include +#include #include "common.h" #include "driver.h" @@ -364,7 +364,7 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) /* copy ifname and take a pointer to the second to last character */ sep = drv->ifname + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; - /* find the '.' seperating and */ + /* find the '.' separating and */ while (sep > drv->ifname && *sep != '.') sep--; if (sep <= drv->ifname) { wpa_printf(MSG_INFO, "%s: No . pair in " diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index fb24673..74dedb2 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -34,6 +34,8 @@ #include "common/ieee802_11_defs.h" #include "crypto/sha1.h" #include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" +#include "wps/wps.h" #include "driver.h" @@ -87,7 +89,6 @@ struct wpa_driver_test_data { int use_associnfo; u8 assoc_wpa_ie[80]; size_t assoc_wpa_ie_len; - int use_mlme; int associated; u8 *probe_req_ie; size_t probe_req_ie_len; @@ -107,6 +108,20 @@ struct wpa_driver_test_data { unsigned int remain_on_channel_duration; int current_freq; + + struct p2p_data *p2p; + unsigned int off_channel_freq; + struct wpabuf *pending_action_tx; + u8 pending_action_src[ETH_ALEN]; + u8 pending_action_dst[ETH_ALEN]; + u8 pending_action_bssid[ETH_ALEN]; + unsigned int pending_action_freq; + unsigned int pending_action_no_cck; + unsigned int pending_listen_freq; + unsigned int pending_listen_duration; + int pending_p2p_scan; + struct sockaddr *probe_from; + socklen_t probe_from_len; }; @@ -116,6 +131,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, static void wpa_driver_test_close_test_socket( struct wpa_driver_test_data *drv); static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); static void test_driver_free_bss(struct test_driver_bss *bss) @@ -159,7 +175,7 @@ test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from, static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) + const u8 *own_addr, u32 flags) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -469,6 +485,34 @@ static int wpa_driver_test_send_mlme(void *priv, const u8 *data, event.tx_status.ack = ret >= 0; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + if (drv->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "no pending operation"); + return ret; + } + + if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "unknown destination address"); + return ret; + } + + wpabuf_free(drv->pending_action_tx); + drv->pending_action_tx = NULL; + + p2p_send_action_cb(drv->p2p, drv->pending_action_freq, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + ret >= 0); + } +#endif /* CONFIG_P2P */ + return ret; } @@ -515,6 +559,10 @@ static void test_driver_scan(struct wpa_driver_test_data *drv, event.rx_probe_req.ie = ie; event.rx_probe_req.ie_len = ielen; wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); +#endif /* CONFIG_P2P */ } dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { @@ -624,7 +672,7 @@ static void test_driver_assoc(struct wpa_driver_test_data *drv, sendto(drv->test_socket, cmd, strlen(cmd), 0, (struct sockaddr *) from, fromlen); - drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen); + drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0); } @@ -841,7 +889,8 @@ static int test_driver_set_generic_elem(void *priv, static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { struct test_driver_bss *bss = priv; @@ -1015,7 +1064,8 @@ static int test_driver_bss_remove(void *priv, const char *ifname) static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, - char *force_ifname, u8 *if_addr) + char *force_ifname, u8 *if_addr, + const char *bridge) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -1033,7 +1083,8 @@ static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, sizeof(drv->alloc_iface_idx), if_addr + 1, ETH_ALEN - 1); } - if (type == WPA_IF_AP_BSS) + if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, drv_priv); return 0; @@ -1044,27 +1095,23 @@ static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname) { wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); - if (type == WPA_IF_AP_BSS) + if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) return test_driver_bss_remove(priv, ifname); return 0; } -static int test_driver_valid_bss_mask(void *priv, const u8 *addr, - const u8 *mask) -{ - return 0; -} - - static int test_driver_set_ssid(void *priv, const u8 *buf, int len) { struct test_driver_bss *bss = priv; wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); + if (len < 0) + return -1; wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); - if (len < 0 || (size_t) len > sizeof(bss->ssid)) + if ((size_t) len > sizeof(bss->ssid)) return -1; os_memcpy(bss->ssid, buf, len); @@ -1178,6 +1225,7 @@ static void * test_driver_init(struct hostapd_data *hapd, return NULL; drv->ap = 1; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + drv->global = params->global_priv; bss->bss_ctx = hapd; os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); @@ -1271,7 +1319,23 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { + struct wpa_driver_test_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + if (drv->pending_p2p_scan && drv->p2p) { +#ifdef CONFIG_P2P + size_t i; + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *bss = drv->scanres[i]; + if (p2p_scan_res_handler(drv->p2p, bss->bssid, + bss->freq, bss->level, + (const u8 *) (bss + 1), + bss->ie_len) > 0) + return; + } + p2p_scan_res_handled(drv->p2p); +#endif /* CONFIG_P2P */ + return; + } wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -1487,6 +1551,7 @@ static int wpa_driver_test_associate( __func__, priv, params->freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); if (params->bssid) { wpa_printf(MSG_DEBUG, " bssid=" MACSTR, MAC2STR(params->bssid)); @@ -1848,6 +1913,8 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, { int freq = 0, own_freq; union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; struct test_driver_bss *bss; bss = dl_list_first(&drv->bss, struct test_driver_bss, list); @@ -1885,23 +1952,45 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, event.mlme_rx.freq = freq; wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); - if (drv->probe_req_report && data_len >= 24) { - const struct ieee80211_mgmt *mgmt; - u16 fc; + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); - mgmt = (const struct ieee80211_mgmt *) data; - fc = le_to_host16(mgmt->frame_control); + if (drv->probe_req_report && data_len >= 24) { if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; event.rx_probe_req.ie = mgmt->u.probe_req.variable; event.rx_probe_req.ie_len = data_len - (mgmt->u.probe_req.variable - data); wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, mgmt->sa, + mgmt->da, mgmt->bssid, + event.rx_probe_req.ie, + event.rx_probe_req.ie_len); +#endif /* CONFIG_P2P */ } } + +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + size_t hdr_len; + hdr_len = (const u8 *) + &mgmt->u.action.u.vs_public_action.action - data; + p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + data_len - hdr_len, freq); + } +#endif /* CONFIG_P2P */ + } @@ -1917,6 +2006,29 @@ static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, bss = dl_list_first(&drv->bss, struct test_driver_bss, list); /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ +#ifdef CONFIG_P2P + if (drv->probe_req_report && drv->p2p && data_len) { + const char *d = (const char *) data; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + + if (hwaddr_aton(d, sa)) + return; + d += 18; + while (*d == ' ') + d++; + ielen = os_strlen(d) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(d, ie, ielen) < 0) + ielen = 0; + drv->probe_from = from; + drv->probe_from_len = fromlen; + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); + drv->probe_from = NULL; + } +#endif /* CONFIG_P2P */ if (!drv->ibss) return; @@ -2073,6 +2185,12 @@ static void wpa_driver_test_deinit(void *priv) struct test_client_socket *cli, *prev; int i; +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_deinit(drv->p2p); + wpabuf_free(drv->pending_action_tx); +#endif /* CONFIG_P2P */ + cli = drv->cli; while (cli) { prev = cli; @@ -2269,12 +2387,12 @@ static int wpa_driver_test_set_param(void *priv, const char *param) drv->use_associnfo = 1; } -#ifdef CONFIG_CLIENT_MLME - if (os_strstr(param, "use_mlme=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME"); - drv->use_mlme = 1; + if (os_strstr(param, "p2p_mgmt=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " + "management"); + if (wpa_driver_test_init_p2p(drv) < 0) + return -1; } -#endif /* CONFIG_CLIENT_MLME */ return 0; } @@ -2382,9 +2500,12 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; - if (drv->use_mlme) - capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + if (drv->p2p) + capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; capa->flags |= WPA_DRIVER_FLAGS_AP; + capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; capa->max_scan_ssids = 2; capa->max_remain_on_chan = 60000; @@ -2408,50 +2529,6 @@ static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, } -static int wpa_driver_test_set_channel(void *priv, - enum hostapd_hw_mode phymode, - int chan, int freq) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", - __func__, phymode, chan, freq); - drv->current_freq = freq; - return 0; -} - - -static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr, - const u8 *supp_rates, - size_t supp_rates_len) -{ - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); - return 0; -} - - -static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr) -{ - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); - return 0; -} - - -static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - return 0; -} - - -static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) -{ - wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); - return 0; -} - - static void * wpa_driver_test_global_init(void) { struct wpa_driver_test_global *global; @@ -2591,9 +2668,11 @@ static int wpa_driver_test_set_freq(void *priv, static int wpa_driver_test_send_action(void *priv, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, + int no_cck) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -2632,6 +2711,33 @@ static int wpa_driver_test_send_action(void *priv, unsigned int freq, } +#ifdef CONFIG_P2P +static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + + if (drv->pending_action_tx == NULL) + return; + + if (drv->off_channel_freq != drv->pending_action_freq) { + wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " + "waiting for another freq=%u", + drv->pending_action_freq); + return; + } + wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " + MACSTR, MAC2STR(drv->pending_action_dst)); + wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + wpabuf_head(drv->pending_action_tx), + wpabuf_len(drv->pending_action_tx), + drv->pending_action_no_cck); +} +#endif /* CONFIG_P2P */ + + static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; @@ -2642,9 +2748,13 @@ static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) os_memset(&data, 0, sizeof(data)); data.remain_on_channel.freq = drv->remain_on_channel_freq; data.remain_on_channel.duration = drv->remain_on_channel_duration; - wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); + + if (drv->p2p) + drv->off_channel_freq = 0; drv->remain_on_channel_freq = 0; + + wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); } @@ -2675,6 +2785,18 @@ static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); +#ifdef CONFIG_P2P + if (drv->p2p) { + drv->off_channel_freq = drv->remain_on_channel_freq; + test_send_action_cb(drv, NULL); + if (drv->off_channel_freq == drv->pending_listen_freq) { + p2p_listen_cb(drv->p2p, drv->pending_listen_freq, + drv->pending_listen_duration); + drv->pending_listen_freq = 0; + } + } +#endif /* CONFIG_P2P */ + return 0; } @@ -2702,6 +2824,461 @@ static int wpa_driver_test_probe_req_report(void *priv, int report) } +#ifdef CONFIG_P2P + +static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL); +} + + +static int wpa_driver_test_p2p_stop_find(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_stop_find(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_listen(drv->p2p, timeout); +} + + +static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, + int wps_method, int go_intent, + const u8 *own_interface_addr, + unsigned int force_freq, + int persistent_group) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " + "go_intent=%d " + "own_interface_addr=" MACSTR " force_freq=%u " + "persistent_group=%d)", + __func__, MAC2STR(peer_addr), wps_method, go_intent, + MAC2STR(own_interface_addr), force_freq, persistent_group); + if (!drv->p2p) + return -1; + return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, + own_interface_addr, force_freq, persistent_group); +} + + +static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", + __func__, MAC2STR(peer_addr)); + if (!drv->p2p) + return -1; + p2p_wps_success_cb(drv->p2p, peer_addr); + return 0; +} + + +static int wpa_driver_test_p2p_group_formation_failed(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_group_formation_failed(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_set_params(void *priv, + const struct p2p_params *params) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 || + p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 || + p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type, + params->num_sec_dev_types) < 0) + return -1; + return 0; +} + + +static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id) +{ + struct wpa_driver_test_data *drv = ctx; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + size_t ielen; + + wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)", + __func__, type, freq); + + os_memset(¶ms, 0, sizeof(params)); + + /* P2P Wildcard SSID */ + params.num_ssids = 1; + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + +#if 0 /* TODO: WPS IE */ + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, + WPS_REQ_ENROLLEE); +#else + wps_ie = wpabuf_alloc(1); +#endif + if (wps_ie == NULL) + return -1; + + ielen = p2p_scan_ie_buf_len(drv->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + return -1; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(drv->p2p, ies, dev_id); + + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + switch (type) { + case P2P_SCAN_SOCIAL: + params.freqs = social_channels; + break; + case P2P_SCAN_FULL: + break; + case P2P_SCAN_SPECIFIC: + social_channels[0] = freq; + social_channels[1] = 0; + params.freqs = social_channels; + break; + case P2P_SCAN_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + drv->pending_p2p_scan = 1; + ret = wpa_driver_test_scan(drv, ¶ms); + + wpabuf_free(ies); + + return ret; +} + + +static int test_send_action(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR + " bssid=" MACSTR " len=%d", + __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + if (freq <= 0) { + wpa_printf(MSG_WARNING, "P2P: No frequency specified for " + "action frame TX"); + return -1; + } + + if (drv->pending_action_tx) { + wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " + "to " MACSTR, MAC2STR(drv->pending_action_dst)); + wpabuf_free(drv->pending_action_tx); + } + drv->pending_action_tx = wpabuf_alloc(len); + if (drv->pending_action_tx == NULL) + return -1; + wpabuf_put_data(drv->pending_action_tx, buf, len); + os_memcpy(drv->pending_action_src, src, ETH_ALEN); + os_memcpy(drv->pending_action_dst, dst, ETH_ALEN); + os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN); + drv->pending_action_freq = freq; + drv->pending_action_no_cck = 1; + + if (drv->off_channel_freq == freq) { + /* Already on requested channel; send immediately */ + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + eloop_cancel_timeout(test_send_action_cb, drv, NULL); + eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL); + return 0; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " + "once the driver gets to the requested channel"); + if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " + "to remain on channel (%u MHz) for Action " + "Frame TX", freq); + return -1; + } + + return 0; +} + + +static void test_send_action_done(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_completed.res = res; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event); +} + + +static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src)); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_req_rx.src = src; + event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event); +} + + +static void test_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, int new_device) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x)", + __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, devtype, + sizeof(devtype)), + info->device_name, info->config_methods, info->dev_capab, + info->group_capab); + + os_memset(&event, 0, sizeof(event)); + event.p2p_dev_found.addr = addr; + event.p2p_dev_found.dev_addr = info->p2p_device_addr; + event.p2p_dev_found.pri_dev_type = info->pri_dev_type; + event.p2p_dev_found.dev_name = info->device_name; + event.p2p_dev_found.config_methods = info->config_methods; + event.p2p_dev_found.dev_capab = info->dev_capab; + event.p2p_dev_found.group_capab = info->group_capab; + wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event); +} + + +static int test_start_listen(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)", + __func__, freq, duration); + + if (wpa_driver_test_probe_req_report(drv, 1) < 0) + return -1; + + drv->pending_listen_freq = freq; + drv->pending_listen_duration = duration; + + if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) { + drv->pending_listen_freq = 0; + return -1; + } + + return 0; +} + + +static void test_stop_listen(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static int test_send_probe_resp(void *ctx, const struct wpabuf *buf) +{ + struct wpa_driver_test_data *drv = ctx; + char resp[512], *pos, *end; + int ret; + const struct ieee80211_mgmt *mgmt; + const u8 *ie, *ie_end; + + wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf); + if (wpabuf_len(buf) < 24) + return -1; + if (!drv->probe_from) { + wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__); + return -1; + } + + pos = resp; + end = resp + sizeof(resp); + + mgmt = wpabuf_head(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(mgmt->bssid)); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + + ie = mgmt->u.probe_resp.variable; + ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf); + if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID || + ie + 2 + ie[1] > ie_end) + return -1; + pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]); + + ret = os_snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie); + + sendto(drv->test_socket, resp, pos - resp, 0, + drv->probe_from, drv->probe_from_len); + + return 0; +} + + +static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + + +static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + +#endif /* CONFIG_P2P */ + + +static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv) +{ +#ifdef CONFIG_P2P + struct p2p_config p2p; + unsigned int r; + int i; + + os_memset(&p2p, 0, sizeof(p2p)); + p2p.msg_ctx = drv->ctx; + p2p.cb_ctx = drv; + p2p.p2p_scan = test_p2p_scan; + p2p.send_action = test_send_action; + p2p.send_action_done = test_send_action_done; + p2p.go_neg_completed = test_go_neg_completed; + p2p.go_neg_req_rx = test_go_neg_req_rx; + p2p.dev_found = test_dev_found; + p2p.start_listen = test_start_listen; + p2p.stop_listen = test_stop_listen; + p2p.send_probe_resp = test_send_probe_resp; + p2p.sd_request = test_sd_request; + p2p.sd_response = test_sd_response; + p2p.prov_disc_req = test_prov_disc_req; + p2p.prov_disc_resp = test_prov_disc_resp; + + os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN); + + p2p.reg_class = 12; /* TODO: change depending on location */ + /* + * Pick one of the social channels randomly as the listen + * channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.channel = 1 + (r % 3) * 5; + + /* TODO: change depending on location */ + p2p.op_reg_class = 12; + /* + * For initial tests, pick the operation channel randomly. + * TODO: Use scan results (etc.) to select the best channel. + */ + p2p.op_channel = 1 + r % 11; + + os_memcpy(p2p.country, "US ", 3); + + /* FIX: fetch available channels from the driver */ + p2p.channels.reg_classes = 1; + p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */ + p2p.channels.reg_class[0].channels = 11; + for (i = 0; i < 11; i++) + p2p.channels.reg_class[0].channel[i] = i + 1; + + p2p.max_peers = 100; + + drv->p2p = p2p_init(&p2p); + if (drv->p2p == NULL) + return -1; + return 0; +#else /* CONFIG_P2P */ + wpa_printf(MSG_INFO, "driver_test: P2P support not included"); + return -1; +#endif /* CONFIG_P2P */ +} + + const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", @@ -2715,7 +3292,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, .if_add = test_driver_if_add, .if_remove = test_driver_if_remove, - .valid_bss_mask = test_driver_valid_bss_mask, .hapd_set_ssid = test_driver_set_ssid, .set_privacy = test_driver_set_privacy, .set_sta_vlan = test_driver_set_sta_vlan, @@ -2734,11 +3310,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .get_mac_addr = wpa_driver_test_get_mac_addr, .send_eapol = wpa_driver_test_send_eapol, .mlme_setprotection = wpa_driver_test_mlme_setprotection, - .set_channel = wpa_driver_test_set_channel, - .set_ssid = wpa_driver_test_set_ssid, - .set_bssid = wpa_driver_test_set_bssid, - .mlme_add_sta = wpa_driver_test_mlme_add_sta, - .mlme_remove_sta = wpa_driver_test_mlme_remove_sta, .get_scan_results2 = wpa_driver_test_get_scan_results2, .global_init = wpa_driver_test_global_init, .global_deinit = wpa_driver_test_global_deinit, @@ -2750,4 +3321,14 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .remain_on_channel = wpa_driver_test_remain_on_channel, .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, .probe_req_report = wpa_driver_test_probe_req_report, +#ifdef CONFIG_P2P + .p2p_find = wpa_driver_test_p2p_find, + .p2p_stop_find = wpa_driver_test_p2p_stop_find, + .p2p_listen = wpa_driver_test_p2p_listen, + .p2p_connect = wpa_driver_test_p2p_connect, + .wps_success_cb = wpa_driver_test_wps_success_cb, + .p2p_group_formation_failed = + wpa_driver_test_p2p_group_formation_failed, + .p2p_set_params = wpa_driver_test_p2p_set_params, +#endif /* CONFIG_P2P */ }; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 2614f23..b980c15 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -20,7 +20,9 @@ #include "includes.h" #include +#include #include +#include #include #include "wireless_copy.h" @@ -31,6 +33,7 @@ #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" +#include "rfkill.h" #include "driver.h" #include "driver_wext.h" @@ -561,10 +564,28 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, del ? "removed" : "added"); if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { - if (del) + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already set - ignore event"); + return; + } drv->if_removed = 1; - else + } else { + if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Interface %s " + "does not exist - ignore " + "RTM_NEWLINK", + drv->ifname); + return; + } + if (!drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already cleared - ignore event"); + return; + } drv->if_removed = 0; + } } wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); @@ -620,6 +641,7 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, struct wpa_driver_wext_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + char namebuf[IFNAMSIZ]; if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) { wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", @@ -634,6 +656,35 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "WEXT: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is down", + namebuf); + } else if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s does not exist", + drv->ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is marked " + "removed", drv->ifname); + } else { + wpa_printf(MSG_DEBUG, "WEXT: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_wext_set_operstate() @@ -687,6 +738,62 @@ static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, } +static void wpa_driver_wext_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_wext_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_wext_data *drv = ctx; + wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void wext_get_phy_name(struct wpa_driver_wext_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; + } + + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } + + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s", + drv->ifname, drv->phyname); +} + + /** * wpa_driver_wext_init - Initialize WE driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -698,6 +805,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) { struct wpa_driver_wext_data *drv; struct netlink_config *cfg; + struct rfkill_config *rcfg; char path[128]; struct stat buf; @@ -711,6 +819,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) if (stat(path, &buf) == 0) { wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); drv->cfg80211 = 1; + wext_get_phy_name(drv); } drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); @@ -731,6 +840,19 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) goto err2; } + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto err3; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available"); + os_free(rcfg); + } + drv->mlme_sock = -1; if (wpa_driver_wext_finish_drv_init(drv) < 0) @@ -741,6 +863,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) return drv; err3: + rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); err2: close(drv->ioctl_sock); @@ -750,10 +873,29 @@ err1: } +static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { - if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) - return -1; + int send_rfkill_event = 0; + + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable " + "interface '%s' due to rfkill", + drv->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "WEXT: Could not set " + "interface '%s' UP", drv->ifname); + return -1; + } + } /* * Make sure that the driver does not have any obsolete PMKID entries. @@ -795,6 +937,11 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 1, IF_OPER_DORMANT); + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill, + drv, drv->ctx); + } + return 0; } @@ -822,6 +969,7 @@ void wpa_driver_wext_deinit(void *priv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); if (drv->mlme_sock >= 0) eloop_unregister_read_sock(drv->mlme_sock); @@ -894,7 +1042,7 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ - timeout = 5; + timeout = 10; if (drv->scan_complete_events) { /* * The driver seems to deliver SIOCGIWSCAN events to notify @@ -1040,7 +1188,8 @@ static void wext_get_scan_freq(struct iw_event *iwe, } -static void wext_get_scan_qual(struct iw_event *iwe, +static void wext_get_scan_qual(struct wpa_driver_wext_data *drv, + struct iw_event *iwe, struct wext_scan_data *res) { res->res.qual = iwe->u.qual.qual; @@ -1054,6 +1203,14 @@ static void wext_get_scan_qual(struct iw_event *iwe, res->res.flags |= WPA_SCAN_NOISE_INVALID; if (iwe->u.qual.updated & IW_QUAL_DBM) res->res.flags |= WPA_SCAN_LEVEL_DBM; + if ((iwe->u.qual.updated & IW_QUAL_DBM) || + ((iwe->u.qual.level != 0) && + (iwe->u.qual.level > drv->max_level))) { + if (iwe->u.qual.level >= 64) + res->res.level -= 0x100; + if (iwe->u.qual.noise >= 64) + res->res.noise -= 0x100; + } } @@ -1255,7 +1412,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, tmp[res->num++] = r; res->res = tmp; } - + /** * wpa_driver_wext_get_scan_results - Fetch the latest scan results @@ -1265,7 +1422,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) { struct wpa_driver_wext_data *drv = priv; - size_t ap_num = 0, len; + size_t len; int first; u8 *res_buf; struct iw_event iwe_buf, *iwe = &iwe_buf; @@ -1277,7 +1434,6 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) if (res_buf == NULL) return NULL; - ap_num = 0; first = 1; res = os_zalloc(sizeof(*res)); @@ -1329,7 +1485,7 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) wext_get_scan_freq(iwe, &data); break; case IWEVQUAL: - wext_get_scan_qual(iwe, &data); + wext_get_scan_qual(drv, iwe, &data); break; case SIOCGIWENCODE: wext_get_scan_encode(iwe, &data); @@ -1427,6 +1583,8 @@ static int wpa_driver_wext_get_range(void *priv) "assuming WPA is not supported"); } + drv->max_level = range->max_qual.level; + os_free(range); return 0; } @@ -1498,8 +1656,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, iwr.u.encoding.pointer = (caddr_t) ext; iwr.u.encoding.length = sizeof(*ext) + key_len; - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + if (addr == NULL || is_broadcast_ether_addr(addr)) ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; if (set_tx) ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; @@ -1702,8 +1859,10 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +#ifndef ANDROID u8 ssid[32]; int i; +#endif /* ANDROID */ /* * Only force-disconnect when the card is in infrastructure mode, @@ -1718,32 +1877,39 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) } if (iwr.u.mode == IW_MODE_INFRA) { + /* Clear the BSSID selection */ + if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID " + "selection on disconnect"); + } + +#ifndef ANDROID if (drv->cfg80211) { /* * cfg80211 supports SIOCSIWMLME commands, so there is * no need for the random SSID hack, but clear the - * BSSID and SSID. + * SSID. */ - if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || - wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { + if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to clear " - "to disconnect"); + "SSID on disconnect"); } return; } + /* - * Clear the BSSID selection and set a random SSID to make sure - * the driver will not be trying to associate with something - * even if it does not understand SIOCSIWMLME commands (or - * tries to associate automatically after deauth/disassoc). + * Set a random SSID to make sure the driver will not be trying + * to associate with something even if it does not understand + * SIOCSIWMLME commands (or tries to associate automatically + * after deauth/disassoc). */ for (i = 0; i < 32; i++) ssid[i] = rand() & 0xFF; - if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || - wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " - "BSSID/SSID to disconnect"); + "SSID to disconnect"); } +#endif /* ANDROID */ } } @@ -2167,6 +2333,13 @@ int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) } +static const char * wext_get_radio_name(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return drv->phyname; +} + + const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", @@ -2186,4 +2359,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .flush_pmkid = wpa_driver_wext_flush_pmkid, .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, + .get_radio_name = wext_get_radio_name, }; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index 602c7e1..89c13eb 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -23,9 +23,12 @@ struct wpa_driver_wext_data { int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; + char phyname[32]; int ifindex; int ifindex2; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; @@ -45,6 +48,8 @@ struct wpa_driver_wext_data { int scan_complete_events; int cfg80211; /* whether driver is using cfg80211 */ + + u8 max_level; }; int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 2b197f0..618db26 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -24,6 +24,9 @@ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include +#endif /* __sun__ */ #include "common.h" #include "eloop.h" @@ -311,7 +314,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) static int wired_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) + const u8 *own_addr, u32 flags) { struct wpa_driver_wired_data *drv = priv; struct ieee8023_hdr *hdr; @@ -462,6 +465,10 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) struct ifreq ifr; int s; +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index bffbbde..b710778 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -24,25 +24,12 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_HERMES -extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL -extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_NDISWRAPPER -/* driver_ndiswrapper.c */ -extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops; -#endif /* CONFIG_DRIVER_NDISWRAPPER */ #ifdef CONFIG_DRIVER_BROADCOM extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ #endif /* CONFIG_DRIVER_BROADCOM */ -#ifdef CONFIG_DRIVER_IPW -extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */ -#endif /* CONFIG_DRIVER_IPW */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ @@ -87,24 +74,12 @@ struct wpa_driver_ops *wpa_drivers[] = #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_HERMES - &wpa_driver_hermes_ops, -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL - &wpa_driver_atmel_ops, -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_NDISWRAPPER - &wpa_driver_ndiswrapper_ops, -#endif /* CONFIG_DRIVER_NDISWRAPPER */ #ifdef CONFIG_DRIVER_BROADCOM &wpa_driver_broadcom_ops, #endif /* CONFIG_DRIVER_BROADCOM */ -#ifdef CONFIG_DRIVER_IPW - &wpa_driver_ipw_ops, -#endif /* CONFIG_DRIVER_IPW */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index b76b229..b1f70e0 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -1,3 +1,15 @@ +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = + ##### COMMON DRIVERS ifdef CONFIG_DRIVER_HOSTAP @@ -31,11 +43,23 @@ NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y -DRV_LIBS += -lnl +NEED_RFKILL=y + +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif -ifdef CONFIG_LIBNL20 -DRV_LIBS += -lnl-genl -DRV_CFLAGS += -DCONFIG_LIBNL20 + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif endif endif @@ -77,24 +101,7 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT CONFIG_WIRELESS_EXTENSION=y NEED_NETLINK=y NEED_LINUX_IOCTL=y -endif - -ifdef CONFIG_DRIVER_HERMES -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES -DRV_WPA_OBJS += ../src/drivers/driver_hermes.o -CONFIG_WIRELESS_EXTENSION=y -endif - -ifdef CONFIG_DRIVER_ATMEL -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL -DRV_WPA_OBJS += ../src/drivers/driver_atmel.o -CONFIG_WIRELESS_EXTENSION=y -endif - -ifdef CONFIG_DRIVER_NDISWRAPPER -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER -DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o -CONFIG_WIRELESS_EXTENSION=y +NEED_RFKILL=y endif ifdef CONFIG_DRIVER_RALINK @@ -109,12 +116,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o endif -ifdef CONFIG_DRIVER_IPW -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW -DRV_WPA_OBJS += ../src/drivers/driver_ipw.o -CONFIG_WIRELESS_EXTENSION=y -endif - ifdef CONFIG_DRIVER_NDIS DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS DRV_WPA_OBJS += ../src/drivers/driver_ndis.o @@ -152,6 +153,7 @@ endif ifdef CONFIG_WIRELESS_EXTENSION DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION DRV_WPA_OBJS += ../src/drivers/driver_wext.o +NEED_RFKILL=y endif ifdef NEED_NETLINK @@ -162,6 +164,10 @@ ifdef NEED_LINUX_IOCTL DRV_OBJS += ../src/drivers/linux_ioctl.o endif +ifdef NEED_RFKILL +DRV_OBJS += ../src/drivers/rfkill.o +endif + ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk new file mode 100644 index 0000000..2d1ad9d --- /dev/null +++ b/src/drivers/drivers.mk @@ -0,0 +1,190 @@ +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = + +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_OBJS += src/drivers/driver_hostap.c +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += src/drivers/driver_wired.c +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_OBJS += src/drivers/driver_madwifi.c +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += src/drivers/driver_nl80211.c +DRV_OBJS += src/utils/radiotap.c +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y + +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += src/drivers/driver_bsd.c +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += src/drivers/driver_test.c +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += src/drivers/driver_none.c +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += src/drivers/driver_atheros.c +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y +endif + +ifdef CONFIG_DRIVER_RALINK +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK +DRV_WPA_OBJS += src/drivers/driver_ralink.c +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_BROADCOM +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM +DRV_WPA_OBJS += src/drivers/driver_broadcom.c +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += src/drivers/driver_ndis.c +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += src/drivers/driver_ndis_.c +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_OSX +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX +DRV_WPA_OBJS += src/drivers/driver_osx.c +DRV_WPA_LDFLAGS += -framework CoreFoundation +DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 +endif + +ifdef CONFIG_DRIVER_IPHONE +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE +DRV_WPA_OBJS += src/drivers/driver_iphone.c +DRV_WPA_OBJS += src/drivers/MobileApple80211.c +DRV_WPA_LDFLAGS += -framework CoreFoundation +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += src/drivers/driver_roboswitch.c +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += src/drivers/driver_wext.c +NEED_RFKILL=y +endif + +ifdef NEED_NETLINK +DRV_OBJS += src/drivers/netlink.c +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += src/drivers/linux_ioctl.c +endif + +ifdef NEED_RFKILL +DRV_OBJS += src/drivers/rfkill.c +endif + +ifdef CONFIG_DRIVER_CUSTOM +DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM +endif + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 0d6cf54..d7501cf 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -24,6 +24,7 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) { struct ifreq ifr; + int ret; if (sock < 0) return -1; @@ -32,9 +33,10 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", ifname, strerror(errno)); - return -1; + return ret; } if (dev_up) { @@ -48,15 +50,38 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up) } if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", ifname, strerror(errno)); - return -1; + return ret; } return 0; } +int linux_iface_up(int sock, const char *ifname) +{ + struct ifreq ifr; + int ret; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return ret; + } + + return !!(ifr.ifr_flags & IFF_UP); +} + + int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr) { struct ifreq ifr; diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h index a555738..e0bf673 100644 --- a/src/drivers/linux_ioctl.h +++ b/src/drivers/linux_ioctl.h @@ -16,6 +16,7 @@ #define LINUX_IOCTL_H int linux_set_iface_flags(int sock, const char *ifname, int dev_up); +int linux_iface_up(int sock, const char *ifname); int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); int linux_br_add(int sock, const char *brname); diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c index ad15b1d..6778907 100644 --- a/src/drivers/netlink.c +++ b/src/drivers/netlink.c @@ -34,7 +34,7 @@ static void netlink_receive_link(struct netlink_data *netlink, if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) return; cb(netlink->cfg->ctx, NLMSG_DATA(h), - NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), + (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); } diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h index bcbfbb5..ccf12a5 100644 --- a/src/drivers/netlink.h +++ b/src/drivers/netlink.h @@ -16,6 +16,7 @@ #define NETLINK_H struct netlink_data; +struct ifinfomsg; struct netlink_config { void *ctx; diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 2ea3ede..acd6cfa 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -6,7 +6,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2008 Michael Wu * Copyright 2008 Luis Carlos Cobo - * Copyright 2008 Michael Buesch + * Copyright 2008 Michael Buesch * Copyright 2008, 2009 Luis R. Rodriguez * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe @@ -40,6 +40,76 @@ */ /** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -52,6 +122,8 @@ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -84,14 +156,23 @@ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * - * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a - * %NL80222_CMD_NEW_BEACON message) - * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface - * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, - * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. - * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, - * parameters are like for %NL80211_CMD_SET_BEACON. - * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * @NL80211_CMD_GET_BEACON: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, + * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, + * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, + * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. @@ -109,6 +190,10 @@ * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by + * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. + * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by + * %NL80211_ATTR_MAC. * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC @@ -130,13 +215,13 @@ * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain - * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will * store this as a valid request and then query userspace for it. * - * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * - * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The @@ -155,11 +240,36 @@ * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain + * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT + * if scheduled scan is not running. + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to @@ -256,7 +366,14 @@ * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be * determined by the STATUS_CODE attribute. @@ -274,8 +391,8 @@ * channel for the specified amount of time. This can be used to do * off-channel operations like transmit a Public Action frame and wait for * a response while being associated to an AP on another channel. - * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which - * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be * optionally used to specify additional channel parameters. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds @@ -299,42 +416,111 @@ * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface * and @NL80211_ATTR_TX_RATES the set of allowed rates. * - * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames - * (via @NL80211_CMD_ACTION) for processing in userspace. This command - * requires an interface index and a match attribute containing the first - * few bytes of the frame that should match, e.g. a single byte for only - * a category match or four bytes for vendor frames including the OUI. - * The registration cannot be dropped, but is removed automatically - * when the netlink socket is closed. Multiple registrations can be made. - * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This - * command is used both as a request to transmit an Action frame and as an - * event indicating reception of an Action frame that was not processed in + * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * and a match attribute containing the first few bytes of the frame + * that should match, e.g. a single byte for only a category match or + * four bytes for vendor frames including the OUI. The registration + * cannot be dropped, but is removed automatically when the netlink + * socket is closed. Multiple registrations can be made. + * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. This - * channel has to be the current channel (remain-on-channel or the - * operational channel). When called, this operation returns a cookie - * (%NL80211_ATTR_COOKIE) that will be included with the TX status event - * pertaining to the TX request. - * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame - * transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies + * which channel the frame is to be transmitted or was received. If this + * channel is not the current channel (remain-on-channel or the + * operational channel) the device will switch to the given channel and + * transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. When called, this operation + * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the + * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. + * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This * command is used as an event to indicate the that a trigger level was * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed + * by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame + * notification. This event is used to indicate that an unprotected + * deauthentication frame was dropped when MFP is in use. + * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame + * notification. This event is used to indicate that an unprotected + * disassociation frame was dropped when MFP is in use. + * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On + * reception of this notification, userspace may decide to create a new + * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ enum nl80211_commands { -/* don't change the order or add anything inbetween, this is ABI! */ +/* don't change the order or add anything between, this is ABI! */ NL80211_CMD_UNSPEC, NL80211_CMD_GET_WIPHY, /* can dump */ @@ -354,8 +540,10 @@ enum nl80211_commands { NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, - NL80211_CMD_NEW_BEACON, - NL80211_CMD_DEL_BEACON, + NL80211_CMD_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, @@ -372,8 +560,8 @@ enum nl80211_commands { NL80211_CMD_SET_REG, NL80211_CMD_REQ_SET_REG, - NL80211_CMD_GET_MESH_PARAMS, - NL80211_CMD_SET_MESH_PARAMS, + NL80211_CMD_GET_MESH_CONFIG, + NL80211_CMD_SET_MESH_CONFIG, NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, @@ -418,9 +606,12 @@ enum nl80211_commands { NL80211_CMD_SET_TX_BITRATE_MASK, - NL80211_CMD_REGISTER_ACTION, - NL80211_CMD_ACTION, - NL80211_CMD_ACTION_TX_STATUS, + NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, @@ -428,6 +619,34 @@ enum nl80211_commands { NL80211_CMD_SET_CQM, NL80211_CMD_NOTIFY_CQM, + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + NL80211_CMD_UNPROT_DEAUTHENTICATE, + NL80211_CMD_UNPROT_DISASSOCIATE, + + NL80211_CMD_NEW_PEER_CANDIDATE, + + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + + NL80211_CMD_SET_REKEY_OFFLOAD, + + NL80211_CMD_PMKSA_CANDIDATE, + + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -448,6 +667,11 @@ enum nl80211_commands { #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT +/* source-level API compatibility */ +#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG +#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE + /** * enum nl80211_attrs - nl80211 netlink attributes * @@ -518,7 +742,7 @@ enum nl80211_commands { * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). - * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at @@ -563,8 +787,14 @@ enum nl80211_commands { * * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled scan request, a wiphy attribute. * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements * that can be added to a scan request + * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. * * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive @@ -626,6 +856,15 @@ enum nl80211_commands { * request, the driver will assume that the port is unauthorized until * authorized by user space. Otherwise, port is marked authorized by * default in station mode. + * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -636,18 +875,20 @@ enum nl80211_commands { * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT * event (u16) * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating - * that protected APs should be used. + * that protected APs should be used. This is also used with NEW_BEACON to + * indicate that the BSS is to use protection. * - * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to - * indicate which unicast key ciphers will be used with the connection + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON + * to indicate which unicast key ciphers will be used with the connection * (an array of u32). - * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate - * which group key cipher will be used with the connection (a u32). - * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate - * which WPA version(s) the AP we want to associate with is using + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which group key cipher will be used with the connection (a + * u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which WPA version(s) the AP we want to associate with is using * (a u32 with flags from &enum nl80211_wpa_versions). - * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate - * which key management algorithm(s) to use (an array of u32). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which key management algorithm(s) to use (an array of u32). * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. @@ -684,6 +925,9 @@ enum nl80211_commands { * cache, a wiphy attribute. * * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that + * specifies the maximum duration that can be requested with the + * remain-on-channel operation, in milliseconds, u32. * * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. * @@ -695,7 +939,16 @@ enum nl80211_commands { * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain - * at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION. + * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. * * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. @@ -709,11 +962,220 @@ enum nl80211_commands { * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, * NL80211_CMD_DISASSOCIATE. * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas wether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available + * for configuration as TX antennas via the above parameters. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available + * for configuration as RX antennas via the above parameters. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * + * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * + * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be + * changed once the mesh is active. + * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute + * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as + * defined in &enum nl80211_plink_state. Used when userspace is + * driving the peer link management state machine. + * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * indicate which WoW triggers should be enabled. This is also + * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN + * triggers. + + * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan + * cycles, in msecs. + + * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more + * sets of attributes to match during scheduled scans. Only BSSs + * that match any of the sets will be reported. These are + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If ommited, no filtering is done. + * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * are managed in software: interfaces of these types aren't subject to + * any restrictions in their number or combinations. + * + * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. + * + * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, + * nested array attribute containing an entry for each band, with the entry + * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but + * without the length restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from + * &enum nl80211_feature_flags and is advertised in wiphy information. + * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe + * + * requests while operating in AP-mode. + * This attribute holds a bitmap of the supported protocols for + * offloading (see &enum nl80211_probe_resp_offload_support_attr). + * + * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire + * probe-response frame. The DA field in the 802.11 header is zero-ed out, + * to be filled by the FW. + * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable + * this feature. Currently, only supported in mac80211 drivers. + * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the + * ATTR_HT_CAPABILITY to which attention should be paid. + * Currently, only mac80211 NICs support this feature. + * The values that may be configured are: + * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 + * AMPDU density and AMPDU factor. + * All values are treated as suggestions and may be ignored + * by the driver as required. The actual values may be seen in + * the station debugfs ht_caps file. + * + * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country + * abides to when initiating radiation on DFS channels. A country maps + * to one DFS region. + * + * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of + * up to 16 TIDs. + * + * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be + * used by the drivers which has MLME in firmware and does not have support + * to report per station tx/rx activity to free up the staion entry from + * the list. This needs to be used when the driver advertises the + * capability to timeout the stations. + * + * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); + * this attribute is (depending on the driver capabilities) added to + * received frames indicated with %NL80211_CMD_FRAME. + * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ enum nl80211_attrs { -/* don't change the order or add anything inbetween, this is ABI! */ +/* don't change the order or add anything between, this is ABI! */ NL80211_ATTR_UNSPEC, NL80211_ATTR_WIPHY, @@ -763,7 +1225,7 @@ enum nl80211_attrs { NL80211_ATTR_REG_ALPHA2, NL80211_ATTR_REG_RULES, - NL80211_ATTR_MESH_PARAMS, + NL80211_ATTR_MESH_CONFIG, NL80211_ATTR_BSS_BASIC_RATES, @@ -864,6 +1326,102 @@ enum nl80211_attrs { NL80211_ATTR_LOCAL_STATE_CHANGE, + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + NL80211_ATTR_KEY_DEFAULT_TYPES, + + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + + NL80211_ATTR_MESH_SETUP, + + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + + NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, + + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + + NL80211_ATTR_SCHED_SCAN_INTERVAL, + + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + + NL80211_ATTR_PROBE_RESP_OFFLOAD, + + NL80211_ATTR_PROBE_RESP, + + NL80211_ATTR_DFS_REGION, + + NL80211_ATTR_DISABLE_HT, + NL80211_ATTR_HT_CAPABILITY_MASK, + + NL80211_ATTR_NOACK_MAP, + + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -872,6 +1430,7 @@ enum nl80211_attrs { /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION +#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -899,6 +1458,7 @@ enum nl80211_attrs { #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 @@ -915,12 +1475,16 @@ enum nl80211_attrs { * @NL80211_IFTYPE_ADHOC: independent BSS member * @NL80211_IFTYPE_STATION: managed BSS member * @NL80211_IFTYPE_AP: access point - * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. * @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner * @NL80211_IFTYPE_MAX: highest interface type number currently defined - * @__NL80211_IFTYPE_AFTER_LAST: internal use + * @NUM_NL80211_IFTYPES: number of defined interface types * * These values are used with the %NL80211_ATTR_IFTYPE * to set the type of an interface. @@ -935,10 +1499,12 @@ enum nl80211_iftype { NL80211_IFTYPE_WDS, NL80211_IFTYPE_MONITOR, NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, /* keep last */ - __NL80211_IFTYPE_AFTER_LAST, - NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 }; /** @@ -947,11 +1513,16 @@ enum nl80211_iftype { * Station flags. When a station is added to an AP interface, it is * assumed to be already associated (and hence authenticated.) * + * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, @@ -959,6 +1530,8 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, NL80211_STA_FLAG_MFP, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -1004,6 +1577,36 @@ enum nl80211_rate_info { }; /** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + +/** * enum nl80211_sta_info - station information * * These attribute types are used with %NL80211_ATTR_STA_INFO @@ -1013,14 +1616,26 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) - * @__NL80211_STA_INFO_AFTER_LAST: internal - * @NL80211_STA_INFO_MAX: highest possible station info attribute * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute - * containing info as possible, see &enum nl80211_sta_info_txrate. + * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + * @NL80211_STA_INFO_LLID: the station's mesh LLID + * @NL80211_STA_INFO_PLID: the station's mesh PLID + * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, @@ -1034,6 +1649,12 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_BITRATE, NL80211_STA_INFO_RX_PACKETS, NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -1064,14 +1685,17 @@ enum nl80211_mpath_flags { * information about a mesh path. * * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved - * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination - * @NL80211_ATTR_MPATH_SN: destination sequence number - * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path - * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now - * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in * &enum nl80211_mpath_flags; - * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec - * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defind + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { __NL80211_MPATH_INFO_INVALID, @@ -1100,6 +1724,8 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined + * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ enum nl80211_band_attr { __NL80211_BAND_ATTR_INVALID, @@ -1120,6 +1746,7 @@ enum nl80211_band_attr { /** * enum nl80211_frequency_attr - frequency attributes + * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. @@ -1131,6 +1758,9 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, @@ -1150,9 +1780,13 @@ enum nl80211_frequency_attr { /** * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use */ enum nl80211_bitrate_attr { __NL80211_BITRATE_ATTR_INVALID, @@ -1174,7 +1808,11 @@ enum nl80211_bitrate_attr { * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an * 802.11 country information element with regulatory information it - * thinks we should consider. + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. */ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_CORE, @@ -1208,6 +1846,7 @@ enum nl80211_reg_type { /** * enum nl80211_reg_rule_attr - regulatory rule attributes + * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional * considerations for a given frequency range. These are the * &enum nl80211_reg_rule_flags. @@ -1224,6 +1863,9 @@ enum nl80211_reg_type { * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use */ enum nl80211_reg_rule_attr { __NL80211_REG_RULE_ATTR_INVALID, @@ -1242,6 +1884,26 @@ enum nl80211_reg_rule_attr { }; /** + * enum nl80211_sched_scan_match_attr - scheduled scan match attributes + * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, + * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter + * attribute number currently defined + * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_reg_rule_flags - regulatory rule flags * * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed @@ -1275,11 +1937,31 @@ enum nl80211_reg_rule_flags { * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use */ enum nl80211_survey_info { __NL80211_SURVEY_INFO_INVALID, NL80211_SURVEY_INFO_FREQUENCY, NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, @@ -1319,14 +2001,15 @@ enum nl80211_mntr_flags { /** * enum nl80211_meshconf_params - mesh configuration parameters * - * Mesh configuration parameters + * Mesh configuration parameters. These can be changed while the mesh is + * active. * * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in * millisecond units, used by the Peer Link Open message * - * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in @@ -1366,7 +2049,28 @@ enum nl80211_mntr_flags { * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) * that it takes for an HWMP information element to propagate across the mesh * - * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a + * source mesh point for path selection elements. + * + * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between + * root announcements are transmitted. + * + * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. + * + * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. + * + * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding + * or forwarding entity (default is TRUE - forwarding entity) + * + * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the + * threshold for average signal strength of candidate station to establish + * a peer link. * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * @@ -1388,6 +2092,12 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, NL80211_MESHCONF_HWMP_ROOTMODE, + NL80211_MESHCONF_ELEMENT_TTL, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + NL80211_MESHCONF_FORWARDING, + NL80211_MESHCONF_RSSI_THRESHOLD, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -1395,6 +2105,54 @@ enum nl80211_meshconf_params { }; /** + * enum nl80211_mesh_setup_params - mesh setup parameters + * + * Mesh setup parameters. These are used to start/join a mesh and cannot be + * changed while the mesh is active. + * + * @__NL80211_MESH_SETUP_INVALID: Internal use + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a + * vendor specific path selection algorithm or disable it to use the default + * HWMP. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a + * vendor specific path metric or disable it to use the default Airtime + * metric. + * + * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a + * robust security network ie, or a vendor specific information element that + * vendors will use to identify the path selection methods and metrics in use. + * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * daemon will be authenticating mesh candidates. + * + * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication + * daemon will be securing peer link frames. AMPE is a secured version of Mesh + * Peering Management (MPM) and is implemented with the assistance of a + * userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and key + * management). When the flag is unset (default), the kernel can autonomously + * complete (unsecured) mesh peering without the need of a userspace daemon. + * + * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use + */ +enum nl80211_mesh_setup_params { + __NL80211_MESH_SETUP_INVALID, + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, + NL80211_MESH_SETUP_IE, + NL80211_MESH_SETUP_USERSPACE_AUTH, + NL80211_MESH_SETUP_USERSPACE_AMPE, + + /* keep last */ + __NL80211_MESH_SETUP_ATTR_AFTER_LAST, + NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) @@ -1439,6 +2197,7 @@ enum nl80211_channel_type { * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) @@ -1482,6 +2241,12 @@ enum nl80211_bss { /** * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. */ enum nl80211_bss_status { NL80211_BSS_STATUS_AUTHENTICATED, @@ -1519,11 +2284,14 @@ enum nl80211_auth_type { * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + * @NUM_NL80211_KEYTYPES: number of defined key types */ enum nl80211_key_type { NL80211_KEYTYPE_GROUP, NL80211_KEYTYPE_PAIRWISE, NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES }; /** @@ -1542,6 +2310,23 @@ enum nl80211_wpa_versions { }; /** + * enum nl80211_key_default_types - key default types + * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid + * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default + * unicast key + * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default + * multicast key + * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types + */ +enum nl80211_key_default_types { + __NL80211_KEY_DEFAULT_TYPE_INVALID, + NL80211_KEY_DEFAULT_TYPE_UNICAST, + NL80211_KEY_DEFAULT_TYPE_MULTICAST, + + NUM_NL80211_KEY_DEFAULT_TYPES +}; + +/** * enum nl80211_key_attributes - key attributes * @__NL80211_KEY_INVALID: invalid * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -1554,6 +2339,12 @@ enum nl80211_wpa_versions { * CCMP keys, each six bytes in little endian * @NL80211_KEY_DEFAULT: flag indicating default key * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute */ @@ -1565,6 +2356,8 @@ enum nl80211_key_attributes { NL80211_KEY_SEQ, NL80211_KEY_DEFAULT, NL80211_KEY_DEFAULT_MGMT, + NL80211_KEY_TYPE, + NL80211_KEY_DEFAULT_TYPES, /* keep last */ __NL80211_KEY_AFTER_LAST, @@ -1578,12 +2371,15 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). + * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, + NL80211_TXRATE_MCS, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -1592,8 +2388,8 @@ enum nl80211_tx_rate_attributes { /** * enum nl80211_band - Frequency band - * @NL80211_BAND_2GHZ - 2.4 GHz ISM band - * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_2GHZ: 2.4 GHz ISM band + * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, @@ -1615,6 +2411,8 @@ enum nl80211_ps_state { * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -1623,6 +2421,7 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_RSSI_THOLD, NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -1631,9 +2430,9 @@ enum nl80211_attr_cqm { /** * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event - * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the * configured threshold - * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold */ enum nl80211_cqm_rssi_threshold_event { @@ -1641,4 +2440,359 @@ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, }; + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +/** + * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute + * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x07". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes + * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + */ +enum nl80211_wowlan_packet_pattern_attr { + __NL80211_WOWLAN_PKTPAT_INVALID, + NL80211_WOWLAN_PKTPAT_MASK, + NL80211_WOWLAN_PKTPAT_PATTERN, + + NUM_NL80211_WOWLAN_PKTPAT, + MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, +}; + +/** + * struct nl80211_wowlan_pattern_support - pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the + * capability information given by the kernel to userspace. + */ +struct nl80211_wowlan_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; +} __attribute__((packed)); + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. The matching is done on the MSDU, + * i.e. as though the packet was an 802.3 packet, so the pattern matching + * is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_wowlan_pattern_support. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 + * => allows 8 of AP/GO + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possiblities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existant mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * TX status to the socket error queue when requested with the + * socket option. + * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, +}; + +/** + * enum nl80211_probe_resp_offload_support_attr - optional supported + * protocols for probe-response offloading by the driver/FW. + * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. + * Each enum value represents a bit in the bitmap of supported + * protocols. Typically a subset of probe-requests belonging to a + * supported protocol will be excluded from offload and uploaded + * to the host. + * + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u + */ +enum nl80211_probe_resp_offload_support_attr { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c new file mode 100644 index 0000000..8818311 --- /dev/null +++ b/src/drivers/rfkill.c @@ -0,0 +1,194 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen + * + * 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 + +#include "utils/common.h" +#include "utils/eloop.h" +#include "rfkill.h" + +#define RFKILL_EVENT_SIZE_V1 8 + +struct rfkill_event { + u32 idx; + u8 type; + u8 op; + u8 soft; + u8 hard; +} STRUCT_PACKED; + +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, + RFKILL_TYPE_GPS, + RFKILL_TYPE_FM, + NUM_RFKILL_TYPES, +}; + + +struct rfkill_data { + struct rfkill_config *cfg; + int fd; + int blocked; +}; + + +static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct rfkill_data *rfkill = eloop_ctx; + struct rfkill_event event; + ssize_t len; + int new_blocked; + + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + return; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + return; + } + wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) + return; + + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + new_blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + new_blocked = 1; + } else { + wpa_printf(MSG_INFO, "rfkill: WLAN unblocked"); + new_blocked = 0; + } + + if (new_blocked != rfkill->blocked) { + rfkill->blocked = new_blocked; + if (new_blocked) + rfkill->cfg->blocked_cb(rfkill->cfg->ctx); + else + rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); + } +} + + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg) +{ + struct rfkill_data *rfkill; + struct rfkill_event event; + ssize_t len; + + rfkill = os_zalloc(sizeof(*rfkill)); + if (rfkill == NULL) + return NULL; + + rfkill->cfg = cfg; + rfkill->fd = open("/dev/rfkill", O_RDONLY); + if (rfkill->fd < 0) { + wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control " + "device"); + goto fail; + } + + if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) { + wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: " + "%s", strerror(errno)); + goto fail2; + } + + for (;;) { + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + if (errno == EAGAIN) + break; /* No more entries */ + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + break; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + continue; + } + wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_ADD || + event.type != RFKILL_TYPE_WLAN) + continue; + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + rfkill->blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + rfkill->blocked = 1; + } + } + + eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); + + return rfkill; + +fail2: + close(rfkill->fd); +fail: + os_free(rfkill); + return NULL; +} + + +void rfkill_deinit(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return; + + if (rfkill->fd >= 0) { + eloop_unregister_read_sock(rfkill->fd); + close(rfkill->fd); + } + + os_free(rfkill->cfg); + os_free(rfkill); +} + + +int rfkill_is_blocked(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return 0; + + return rfkill->blocked; +} diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h new file mode 100644 index 0000000..7a984a6 --- /dev/null +++ b/src/drivers/rfkill.h @@ -0,0 +1,31 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen + * + * 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. + */ + +#ifndef RFKILL_H +#define RFKILL_H + +struct rfkill_data; + +struct rfkill_config { + void *ctx; + char ifname[IFNAMSIZ]; + void (*blocked_cb)(void *ctx); + void (*unblocked_cb)(void *ctx); +}; + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg); +void rfkill_deinit(struct rfkill_data *rfkill); +int rfkill_is_blocked(struct rfkill_data *rfkill); + +#endif /* RFKILL_H */ diff --git a/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h index ad76466..d01f4cb 100644 --- a/src/drivers/wireless_copy.h +++ b/src/drivers/wireless_copy.h @@ -1,4 +1,4 @@ -/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18. +/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22. * I have just removed kernel related headers and added some typedefs etc. to * make this easier to include into user space programs. * Jouni Malinen, 2005-03-12. @@ -8,10 +8,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 19 18.3.05 + * Version : 22 16.3.07 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H @@ -77,13 +77,11 @@ /***************************** INCLUDES *****************************/ /* jkm - replaced linux headers with C library headers, added typedefs */ -#if 0 -/* To minimise problems in user space, I might remove those headers - * at some point. Jean II */ -#include /* for "caddr_t" et al */ +#ifdef ANDROID +#include /* for __u* and __s* typedefs */ #include /* for "struct sockaddr" et al */ #include /* for IFNAMSIZ and co... */ -#else +#else /* ANDROID */ #include #include typedef __uint32_t __u32; @@ -94,7 +92,7 @@ typedef __uint8_t __u8; #ifndef __user #define __user #endif /* __user */ -#endif +#endif /* ANDROID */ /***************************** VERSION *****************************/ /* @@ -103,7 +101,7 @@ typedef __uint8_t __u8; * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ -#define WIRELESS_EXT 19 +#define WIRELESS_EXT 22 /* * Changes : @@ -227,6 +225,22 @@ typedef __uint8_t __u8; * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + * + * V19 to V20 + * ---------- + * - RtNetlink requests support (SET/GET) + * + * V20 to V21 + * ---------- + * - Remove (struct net_device *)->get_wireless_stats() + * - Change length in ESSID and NICK to strlen() instead of strlen()+1 + * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers + * - Power/Retry relative values no longer * 100000 + * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI + * + * V21 to V22 + * ---------- + * - Prevent leaking of kernel space in stream on 64 bits. */ /**************************** CONSTANTS ****************************/ @@ -340,7 +354,7 @@ typedef __uint8_t __u8; * separate range because of collisions with other tools such as * 'mii-tool'. * We now have 32 commands, so a bit more space ;-). - * Also, all 'odd' commands are only usable by root and don't return the + * Also, all 'even' commands are only usable by root and don't return the * content of ifr/iwr to user (but you are not obliged to use the set/get * convention, just use every other two command). More details in iwpriv.c. * And I repeat : you are not forced to use them with iwpriv, but you @@ -353,8 +367,10 @@ typedef __uint8_t __u8; #define SIOCIWFIRST 0x8B00 #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = func -/* Even : get (world access), odd : set (root access) */ +/* Odd : get (world access), even : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) #define IW_IS_GET(cmd) ((cmd) & 0x1) @@ -457,6 +473,7 @@ typedef __uint8_t __u8; #define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ /* Statistics flags (bitmask in updated) */ #define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ @@ -467,6 +484,7 @@ typedef __uint8_t __u8; #define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ #define IW_QUAL_LEVEL_INVALID 0x20 #define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ #define IW_QUAL_ALL_INVALID 0x70 /* Frequency flags */ @@ -519,10 +537,12 @@ typedef __uint8_t __u8; #define IW_RETRY_TYPE 0xF000 /* Type of parameter */ #define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ #define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ -#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ +#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */ #define IW_RETRY_MIN 0x0001 /* Value is a minimum */ #define IW_RETRY_MAX 0x0002 /* Value is a maximum */ #define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ +#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */ +#define IW_RETRY_LONG 0x0020 /* Value is for long packets */ /* Scanning request flags */ #define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ @@ -540,6 +560,16 @@ typedef __uint8_t __u8; /* Maximum size of returned data */ #define IW_SCAN_MAX_DATA 4096 /* In bytes */ +/* Scan capability flags - in (struct iw_range *)->scan_capa */ +#define IW_SCAN_CAPA_NONE 0x00 +#define IW_SCAN_CAPA_ESSID 0x01 +#define IW_SCAN_CAPA_BSSID 0x02 +#define IW_SCAN_CAPA_CHANNEL 0x04 +#define IW_SCAN_CAPA_MODE 0x08 +#define IW_SCAN_CAPA_RATE 0x10 +#define IW_SCAN_CAPA_TYPE 0x20 +#define IW_SCAN_CAPA_TIME 0x40 + /* Max number of char in custom event - use multiple of them if needed */ #define IW_CUSTOM_MAX 256 /* In bytes */ @@ -549,6 +579,8 @@ typedef __uint8_t __u8; /* MLME requests (SIOCSIWMLME / struct iw_mlme) */ #define IW_MLME_DEAUTH 0 #define IW_MLME_DISASSOC 1 +#define IW_MLME_AUTH 2 +#define IW_MLME_ASSOC 3 /* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ #define IW_AUTH_INDEX 0x0FFF @@ -576,12 +608,14 @@ typedef __uint8_t __u8; #define IW_AUTH_WPA_VERSION_WPA 0x00000002 #define IW_AUTH_WPA_VERSION_WPA2 0x00000004 -/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT + * values (bit field) */ #define IW_AUTH_CIPHER_NONE 0x00000001 #define IW_AUTH_CIPHER_WEP40 0x00000002 #define IW_AUTH_CIPHER_TKIP 0x00000004 #define IW_AUTH_CIPHER_CCMP 0x00000008 #define IW_AUTH_CIPHER_WEP104 0x00000010 +#define IW_AUTH_CIPHER_AES_CMAC 0x00000020 /* IW_AUTH_KEY_MGMT values (bit field) */ #define IW_AUTH_KEY_MGMT_802_1X 1 @@ -637,7 +671,7 @@ typedef __uint8_t __u8; * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCSIWCOMMIT)) + (cmd - SIOCIWFIRST)) #define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) #define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) /* Event capability constants - event autogenerated by the kernel @@ -676,6 +710,19 @@ struct iw_point __u16 flags; /* Optional params */ }; +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +#include + +struct compat_iw_point { + compat_caddr_t pointer; + __u16 length; + __u16 flags; +}; +#endif +#endif + /* * A frequency * For numbers lower than 10^9, we encode the number in 'm' and @@ -970,6 +1017,9 @@ struct iw_range __u16 old_num_channels; __u8 old_num_frequency; + /* Scan capabilities */ + __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */ + /* Wireless event capability bitmasks */ __u32 event_capa[6]; @@ -1046,7 +1096,7 @@ struct iw_range /* Note : this frequency list doesn't need to fit channel numbers, * because each entry contain its channel index */ - __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ }; /* @@ -1073,7 +1123,7 @@ struct iw_priv_args */ struct iw_event { - __u16 len; /* Real lenght of this stuff */ + __u16 len; /* Real length of this stuff */ __u16 cmd; /* Wireless IOCTL */ union iwreq_data u; /* IOCTL fixed payload */ }; @@ -1096,4 +1146,38 @@ struct iw_event #define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ IW_EV_POINT_OFF) +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +struct __compat_iw_event { + __u16 len; /* Real length of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + compat_caddr_t pointer; +}; +#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) +#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) + +/* Size of the various events for compat */ +#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ) +#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32)) +#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality)) +#define IW_EV_COMPAT_POINT_LEN \ + (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ + IW_EV_COMPAT_POINT_OFF) +#endif +#endif + +/* Size of the Event prefix when packed in stream */ +#define IW_EV_LCP_PK_LEN (4) +/* Size of the various events when packed in stream */ +#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ) +#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32)) +#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality)) +#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4) + #endif /* _LINUX_WIRELESS_H */ diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 0efe7ab..3035301 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -68,6 +68,7 @@ typedef enum { EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */, EAP_TYPE_GPSK = 51 /* RFC 5433 */, + EAP_TYPE_PWD = 52 /* RFC 5931 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c index 3a64b8e..8a701d2 100644 --- a/src/eap_common/eap_peap_common.c +++ b/src/eap_common/eap_peap_common.c @@ -1,6 +1,6 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2011, Jouni Malinen * * 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 @@ -18,9 +18,9 @@ #include "crypto/sha1.h" #include "eap_peap_common.h" -void peap_prfplus(int version, const u8 *key, size_t key_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *buf, size_t buf_len) +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) { unsigned char counter = 0; size_t pos, plen; @@ -75,7 +75,8 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, while (pos < buf_len) { counter++; plen = buf_len - pos; - hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) + return -1; if (plen >= SHA1_MAC_LEN) { os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; @@ -85,4 +86,6 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, } len[0] = SHA1_MAC_LEN; } + + return 0; } diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h index f59afb0..f182078 100644 --- a/src/eap_common/eap_peap_common.h +++ b/src/eap_common/eap_peap_common.h @@ -1,6 +1,6 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2011, Jouni Malinen * * 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 @@ -15,8 +15,8 @@ #ifndef EAP_PEAP_COMMON_H #define EAP_PEAP_COMMON_H -void peap_prfplus(int version, const u8 *key, size_t key_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *buf, size_t buf_len); +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len); #endif /* EAP_PEAP_COMMON_H */ diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c new file mode 100644 index 0000000..0dbdff2 --- /dev/null +++ b/src/eap_common/eap_pwd_common.c @@ -0,0 +1,330 @@ +/* + * EAP server/peer: EAP-pwd shared routines + * Copyright (c) 2010, Dan Harkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD license. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include "common.h" +#include "eap_defs.h" +#include "eap_pwd_common.h" + +/* The random function H(x) = HMAC-SHA256(0^32, x) */ +void H_Init(HMAC_CTX *ctx) +{ + u8 allzero[SHA256_DIGEST_LENGTH]; + + os_memset(allzero, 0, SHA256_DIGEST_LENGTH); + HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256()); +} + + +void H_Update(HMAC_CTX *ctx, const u8 *data, int len) +{ + HMAC_Update(ctx, data, len); +} + + +void H_Final(HMAC_CTX *ctx, u8 *digest) +{ + unsigned int mdlen = SHA256_DIGEST_LENGTH; + + HMAC_Final(ctx, digest, &mdlen); + HMAC_CTX_cleanup(ctx); +} + + +/* a counter-based KDF based on NIST SP800-108 */ +void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen, + u8 *result, int resultbitlen) +{ + HMAC_CTX hctx; + unsigned char digest[SHA256_DIGEST_LENGTH]; + u16 i, ctr, L; + int resultbytelen, len = 0; + unsigned int mdlen = SHA256_DIGEST_LENGTH; + unsigned char mask = 0xff; + + resultbytelen = (resultbitlen + 7)/8; + ctr = 0; + L = htons(resultbitlen); + while (len < resultbytelen) { + ctr++; i = htons(ctr); + HMAC_Init(&hctx, key, keylen, EVP_sha256()); + if (ctr > 1) + HMAC_Update(&hctx, digest, mdlen); + HMAC_Update(&hctx, (u8 *) &i, sizeof(u16)); + HMAC_Update(&hctx, label, labellen); + HMAC_Update(&hctx, (u8 *) &L, sizeof(u16)); + HMAC_Final(&hctx, digest, &mdlen); + if ((len + (int) mdlen) > resultbytelen) + os_memcpy(result + len, digest, resultbytelen - len); + else + os_memcpy(result + len, digest, mdlen); + len += mdlen; + HMAC_CTX_cleanup(&hctx); + } + + /* since we're expanding to a bit length, mask off the excess */ + if (resultbitlen % 8) { + mask <<= (8 - (resultbitlen % 8)); + result[resultbytelen - 1] &= mask; + } +} + + +/* + * compute a "random" secret point on an elliptic curve based + * on the password and identities. + */ +int compute_password_element(EAP_PWD_group *grp, u16 num, + u8 *password, int password_len, + u8 *id_server, int id_server_len, + u8 *id_peer, int id_peer_len, u8 *token) +{ + BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + HMAC_CTX ctx; + unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr; + int nid, is_odd, primebitlen, primebytelen, ret = 0; + + switch (num) { /* from IANA registry for IKE D-H groups */ + case 19: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); + return -1; + } + + grp->pwe = NULL; + grp->order = NULL; + grp->prime = NULL; + + if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); + goto fail; + } + + if (((rnd = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || + ((grp->order = BN_new()) == NULL) || + ((grp->prime = BN_new()) == NULL) || + ((x_candidate = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); + goto fail; + } + + if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) + { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " + "curve"); + goto fail; + } + if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); + goto fail; + } + if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " + "curve"); + goto fail; + } + primebitlen = BN_num_bits(grp->prime); + primebytelen = BN_num_bytes(grp->prime); + if ((prfbuf = os_malloc(primebytelen)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " + "buffer"); + goto fail; + } + os_memset(prfbuf, 0, primebytelen); + ctr = 0; + while (1) { + if (ctr > 10) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " + "point on curve for group %d, something's " + "fishy", num); + goto fail; + } + ctr++; + + /* + * compute counter-mode password value and stretch to prime + * pwd-seed = H(token | peer-id | server-id | password | + * counter) + */ + H_Init(&ctx); + H_Update(&ctx, token, sizeof(u32)); + H_Update(&ctx, id_peer, id_peer_len); + H_Update(&ctx, id_server, id_server_len); + H_Update(&ctx, password, password_len); + H_Update(&ctx, &ctr, sizeof(ctr)); + H_Final(&ctx, pwe_digest); + + BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd); + + eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH, + (unsigned char *) "EAP-pwd Hunting And Pecking", + os_strlen("EAP-pwd Hunting And Pecking"), + prfbuf, primebitlen); + + BN_bin2bn(prfbuf, primebytelen, x_candidate); + + /* + * eap_pwd_kdf() returns a string of bits 0..primebitlen but + * BN_bin2bn will treat that string of bits as a big endian + * number. If the primebitlen is not an even multiple of 8 + * then excessive bits-- those _after_ primebitlen-- so now + * we have to shift right the amount we masked off. + */ + if (primebitlen % 8) + BN_rshift(x_candidate, x_candidate, + (8 - (primebitlen % 8))); + + if (BN_ucmp(x_candidate, grp->prime) >= 0) + continue; + + wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + + /* + * need to unambiguously identify the solution, if there is + * one... + */ + if (BN_is_odd(rnd)) + is_odd = 1; + else + is_odd = 0; + + /* + * solve the quadratic equation, if it's not solvable then we + * don't have a point + */ + if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, + grp->pwe, + x_candidate, + is_odd, NULL)) + continue; + /* + * If there's a solution to the equation then the point must be + * on the curve so why check again explicitly? OpenSSL code + * says this is required by X9.62. We're not X9.62 but it can't + * hurt just to be sure. + */ + if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + continue; + } + + if (BN_cmp(cofactor, BN_value_one())) { + /* make sure the point is not in a small sub-group */ + if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, + cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: cannot " + "multiply generator by order"); + continue; + } + if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is at " + "infinity"); + continue; + } + } + /* if we got here then we have a new generator. */ + break; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); + grp->group_num = num; + if (0) { + fail: + EC_GROUP_free(grp->group); + EC_POINT_free(grp->pwe); + BN_free(grp->order); + BN_free(grp->prime); + os_free(grp); + grp = NULL; + ret = 1; + } + /* cleanliness and order.... */ + BN_free(cofactor); + BN_free(x_candidate); + BN_free(rnd); + os_free(prfbuf); + + return ret; +} + + +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, + BIGNUM *peer_scalar, BIGNUM *server_scalar, + u8 *commit_peer, u8 *commit_server, + u32 *ciphersuite, u8 *msk, u8 *emsk) +{ + HMAC_CTX ctx; + u8 mk[SHA256_DIGEST_LENGTH], *cruft; + u8 session_id[SHA256_DIGEST_LENGTH + 1]; + u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; + int offset; + + if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) + return -1; + + /* + * first compute the session-id = TypeCode | H(ciphersuite | scal_p | + * scal_s) + */ + session_id[0] = EAP_TYPE_PWD; + H_Init(&ctx); + H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32)); + offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(peer_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(grp->order)); + offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(server_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(grp->order)); + H_Final(&ctx, &session_id[1]); + + /* then compute MK = H(k | commit-peer | commit-server) */ + H_Init(&ctx); + offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(k, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(grp->prime)); + H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH); + H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH); + H_Final(&ctx, mk); + + /* stretch the mk with the session-id to get MSK | EMSK */ + eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH, + session_id, SHA256_DIGEST_LENGTH+1, + msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8); + + os_memcpy(msk, msk_emsk, EAP_MSK_LEN); + os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); + + os_free(cruft); + + return 1; +} diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h new file mode 100644 index 0000000..971386d --- /dev/null +++ b/src/eap_common/eap_pwd_common.h @@ -0,0 +1,79 @@ +/* + * EAP server/peer: EAP-pwd shared definitions + * Copyright (c) 2009, Dan Harkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD license. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PWD_COMMON_H +#define EAP_PWD_COMMON_H + +#include +#include +#include +#include +#include + +/* + * definition of a finite cyclic group + * TODO: support one based on a prime field + */ +typedef struct group_definition_ { + u16 group_num; + EC_GROUP *group; + EC_POINT *pwe; + BIGNUM *order; + BIGNUM *prime; +} EAP_PWD_group; + +/* + * EAP-pwd header, included on all payloads + */ +struct eap_pwd_hdr { + u8 l_bit:1; + u8 m_bit:1; + u8 exch:6; + u8 total_length[0]; /* included when l_bit is set */ +} STRUCT_PACKED; + +#define EAP_PWD_OPCODE_ID_EXCH 1 +#define EAP_PWD_OPCODE_COMMIT_EXCH 2 +#define EAP_PWD_OPCODE_CONFIRM_EXCH 3 +#define EAP_PWD_GET_LENGTH_BIT(x) ((x)->lm_exch & 0x80) +#define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80) +#define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40) +#define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40) +#define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f) +#define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (y)) + +/* EAP-pwd-ID payload */ +struct eap_pwd_id { + be16 group_num; + u8 random_function; +#define EAP_PWD_DEFAULT_RAND_FUNC 1 + u8 prf; +#define EAP_PWD_DEFAULT_PRF 1 + u8 token[4]; + u8 prep; +#define EAP_PWD_PREP_NONE 0 +#define EAP_PWD_PREP_MS 1 + u8 identity[0]; /* length inferred from payload */ +} STRUCT_PACKED; + +/* common routines */ +int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, + int, u8 *); +int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, + u8 *, u8 *, u32 *, u8 *, u8 *); +void H_Init(HMAC_CTX *); +void H_Update(HMAC_CTX *, const u8 *, int); +void H_Final(HMAC_CTX *, u8 *); + +#endif /* EAP_PWD_COMMON_H */ diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c index 56b4ded..0b37b0b 100644 --- a/src/eap_common/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -20,6 +20,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "eap_common/eap_defs.h" #include "eap_common/eap_sim_common.h" @@ -1121,8 +1122,8 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, if (pos == NULL) return -1; msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; - if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv, - EAP_SIM_IV_LEN)) { + if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, + EAP_SIM_IV_LEN)) { msg->iv = 0; return -1; } diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index 67754d8..003c288 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -18,6 +18,7 @@ #include "crypto/crypto.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/random.h" #include "ikev2_common.h" @@ -639,7 +640,7 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, phdr->flags = 0; iv = wpabuf_put(msg, iv_len); - if (os_get_random(iv, iv_len)) { + if (random_get_bytes(iv, iv_len)) { wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); return -1; } diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h index c96a070..31a2b0d 100644 --- a/src/eap_common/ikev2_common.h +++ b/src/eap_common/ikev2_common.h @@ -139,7 +139,7 @@ enum { IKEV2_TRANSFORM_ESN = 5 }; -/* IKEv2 Tranform Type 1 (Encryption Algorithm) */ +/* IKEv2 Transform Type 1 (Encryption Algorithm) */ enum { ENCR_DES_IV64 = 1, ENCR_DES = 2, diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index b9f186b..cac85db 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -37,6 +37,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "EAP" #define EAP_MAX_AUTH_ROUNDS 50 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, @@ -146,6 +147,7 @@ SM_STATE(EAP, INITIALIZE) sm->methodState = METHOD_NONE; sm->allowNotifications = TRUE; sm->decision = DECISION_FAIL; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); @@ -268,6 +270,8 @@ SM_STATE(EAP, GET_METHOD) goto nak; } + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); @@ -891,6 +895,11 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + if (imsi_len < 7) { + wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); + return -1; + } + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && @@ -1165,7 +1174,6 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, { struct eap_sm *sm = ctx; char *hash_hex = NULL; - char *cert_hex = NULL; switch (ev) { case TLS_CERT_CHAIN_FAILURE: @@ -1177,6 +1185,9 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->cert_fail.reason_txt); break; case TLS_PEER_CERTIFICATE: + if (!sm->eapol_cb->notify_cert) + break; + if (data->peer_cert.hash) { size_t len = data->peer_cert.hash_len * 2 + 1; hash_hex = os_malloc(len); @@ -1186,31 +1197,15 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->peer_cert.hash_len); } } - wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s'%s%s", - data->peer_cert.depth, data->peer_cert.subject, - hash_hex ? " hash=" : "", hash_hex ? hash_hex : ""); - - if (data->peer_cert.cert) { - size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1; - cert_hex = os_malloc(len); - if (cert_hex == NULL) - break; - wpa_snprintf_hex(cert_hex, len, - wpabuf_head(data->peer_cert.cert), - wpabuf_len(data->peer_cert.cert)); - wpa_msg_ctrl(sm->msg_ctx, MSG_INFO, - WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s' cert=%s", - data->peer_cert.depth, - data->peer_cert.subject, - cert_hex); - } + + sm->eapol_cb->notify_cert(sm->eapol_ctx, + data->peer_cert.depth, + data->peer_cert.subject, + hash_hex, data->peer_cert.cert); break; } os_free(hash_hex); - os_free(cert_hex); } @@ -1241,7 +1236,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->msg_ctx = msg_ctx; - sm->ClientTimeout = 60; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; sm->wps = conf->wps; os_memset(&tlsconf, 0, sizeof(tlsconf)); @@ -1253,6 +1248,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, #endif /* CONFIG_FIPS */ tlsconf.event_cb = eap_peer_sm_tls_event; tlsconf.cb_ctx = sm; + tlsconf.cert_in_cb = conf->cert_in_cb; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " @@ -1477,16 +1473,11 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -typedef enum { - TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD, - TYPE_PASSPHRASE -} eap_ctrl_req_type; - -static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { struct eap_peer_config *config; - char *field, *txt, *tmp; + char *txt = NULL, *tmp; if (sm == NULL) return; @@ -1494,29 +1485,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, if (config == NULL) return; - switch (type) { - case TYPE_IDENTITY: - field = "IDENTITY"; - txt = "Identity"; + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: config->pending_req_identity++; break; - case TYPE_PASSWORD: - field = "PASSWORD"; - txt = "Password"; + case WPA_CTRL_REQ_EAP_PASSWORD: config->pending_req_password++; break; - case TYPE_NEW_PASSWORD: - field = "NEW_PASSWORD"; - txt = "New Password"; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: config->pending_req_new_password++; break; - case TYPE_PIN: - field = "PIN"; - txt = "PIN"; + case WPA_CTRL_REQ_EAP_PIN: config->pending_req_pin++; break; - case TYPE_OTP: - field = "OTP"; + case WPA_CTRL_REQ_EAP_OTP: if (msg) { tmp = os_malloc(msglen + 3); if (tmp == NULL) @@ -1535,9 +1517,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, txt = config->pending_req_otp; } break; - case TYPE_PASSPHRASE: - field = "PASSPHRASE"; - txt = "Private key passphrase"; + case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; default: @@ -1551,6 +1531,13 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, #define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +const char * eap_sm_get_method_name(struct eap_sm *sm) +{ + if (sm->m == NULL) + return "UNKNOWN"; + return sm->m->name; +} + /** * eap_sm_request_identity - Request identity from user (ctrl_iface) @@ -1563,7 +1550,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, */ void eap_sm_request_identity(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); } @@ -1578,7 +1565,7 @@ void eap_sm_request_identity(struct eap_sm *sm) */ void eap_sm_request_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); } @@ -1593,7 +1580,7 @@ void eap_sm_request_password(struct eap_sm *sm) */ void eap_sm_request_new_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); } @@ -1608,7 +1595,7 @@ void eap_sm_request_new_password(struct eap_sm *sm) */ void eap_sm_request_pin(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PIN, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); } @@ -1624,7 +1611,7 @@ void eap_sm_request_pin(struct eap_sm *sm) */ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) { - eap_sm_request(sm, TYPE_OTP, msg, msg_len); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); } @@ -1639,7 +1626,7 @@ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) */ void eap_sm_request_passphrase(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); } @@ -1923,6 +1910,15 @@ const char * eap_get_config_phase2(struct eap_sm *sm) } +int eap_get_config_fragment_size(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return -1; + return config->fragment_size; +} + + /** * eap_key_available - Get key availability (eapKeyAvailable variable) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 40d0b69..f35197f 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -216,11 +216,22 @@ struct eapol_callbacks { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: eapol_ctx from eap_peer_sm_init() call - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); + + /** + * notify_cert - Notification of a peer certificate + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*notify_cert)(void *ctx, int depth, const char *subject, + const char *cert_hash, const struct wpabuf *cert); }; /** @@ -251,6 +262,11 @@ struct eap_config { * This is only used by EAP-WSC and can be left %NULL if not available. */ struct wps_context *wps; + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, @@ -261,6 +277,7 @@ int eap_peer_sm_step(struct eap_sm *sm); void eap_sm_abort(struct eap_sm *sm); int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose); +const char * eap_sm_get_method_name(struct eap_sm *sm); struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted); void eap_sm_request_identity(struct eap_sm *sm); void eap_sm_request_password(struct eap_sm *sm); diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 182f01a..6ac6b64 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -235,21 +235,20 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) static void eap_aka_clear_identities(struct eap_aka_data *data, int id) { - wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", - id & CLEAR_PSEUDONYM ? " pseudonym" : "", - id & CLEAR_REAUTH_ID ? " reauth_id" : "", - id & CLEAR_EAP_ID ? " eap_id" : ""); - if (id & CLEAR_PSEUDONYM) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } - if (id & CLEAR_EAP_ID) { + if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; @@ -257,24 +256,44 @@ static void eap_aka_clear_identities(struct eap_aka_data *data, int id) } -static int eap_aka_learn_ids(struct eap_aka_data *data, +static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); - data->pseudonym_len = attr->next_pseudonym_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", - data->pseudonym, - data->pseudonym_len); + if (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; } if (attr->next_reauth_id) { @@ -283,6 +302,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -880,11 +900,11 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - /* Old reauthentication and pseudonym identities must not be used - * anymore. In other words, if no new identities are received, full - * authentication will be used on next reauthentication. */ - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | - CLEAR_EAP_ID); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new identities are received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -895,7 +915,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_client_error( data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - eap_aka_learn_ids(data, &eattr); + eap_aka_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -1113,7 +1133,7 @@ static struct wpabuf * eap_aka_process_reauthentication( data->msk, data->emsk); } eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - eap_aka_learn_ids(data, &eattr); + eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 5d3e69d..3cfb41a 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -444,8 +444,9 @@ static int eap_fast_phase2_request(struct eap_sm *sm, return 0; } - if (data->phase2_priv == NULL && - eap_fast_init_phase2_method(sm, data) < 0) { + if ((data->phase2_priv == NULL && + eap_fast_init_phase2_method(sm, data) < 0) || + data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; @@ -542,7 +543,7 @@ static struct wpabuf * eap_fast_tlv_pac_ack(void) static struct wpabuf * eap_fast_process_eap_payload_tlv( struct eap_sm *sm, struct eap_fast_data *data, - struct eap_method_ret *ret, const struct eap_hdr *req, + struct eap_method_ret *ret, u8 *eap_payload_tlv, size_t eap_payload_tlv_len) { struct eap_hdr *hdr; @@ -1037,11 +1038,15 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, } else { /* * This is PAC refreshing, i.e., normal authentication that is - * expected to be completed with an EAP-Success. + * expected to be completed with an EAP-Success. However, + * RFC 5422, Section 3.5 allows EAP-Failure to be sent even + * after protected success exchange in case of EAP-Fast + * provisioning, so we better use DECISION_COND_SUCC here + * instead of DECISION_UNCOND_SUCC. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- PAC refreshing completed successfully"); - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; } ret->methodState = METHOD_DONE; return eap_fast_tlv_pac_ack(); @@ -1184,7 +1189,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, if (tlv.eap_payload_tlv) { tmp = eap_fast_process_eap_payload_tlv( - sm, data, ret, req, tlv.eap_payload_tlv, + sm, data, ret, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); resp = wpabuf_concat(resp, tmp); } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index 541cce5..4037288 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -497,6 +497,7 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, *buf = NULL; return; } + *pos = nbuf + (*pos - *buf); *buf = nbuf; *buf_len += need; } diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index f6a1955..5037c60 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -326,7 +327,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, wpabuf_put_be16(resp, data->id_server_len); wpabuf_put_data(resp, data->id_server, data->id_server_len); - if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) { + if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " "for RAND_Peer"); eap_gpsk_state(data, FAILURE); diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index e7c826e..afca611 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -345,6 +345,7 @@ const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len); void eap_clear_config_otp(struct eap_sm *sm); const char * eap_get_config_phase1(struct eap_sm *sm); const char * eap_get_config_phase2(struct eap_sm *sm); +int eap_get_config_fragment_size(struct eap_sm *sm); struct eap_peer_config * eap_get_config(struct eap_sm *sm); void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob); const struct wpa_config_blob * diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index a7c94a4..6a8efcd 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -17,6 +17,7 @@ #include "common.h" #include "crypto/ms_funcs.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_i.h" #define LEAP_VERSION 1 @@ -167,7 +168,7 @@ static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); - if (os_get_random(pos, LEAP_CHALLENGE_LEN)) { + if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " "for challenge"); wpabuf_free(resp); diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 3b0af05..937fd45 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -77,6 +77,8 @@ EapType eap_peer_get_type(const char *name, int *vendor) const char * eap_get_name(int vendor, EapType type) { struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index 384c61b..4330b57 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -109,5 +109,6 @@ int eap_peer_wsc_register(void); int eap_peer_ikev2_register(void); int eap_peer_vendor_test_register(void); int eap_peer_tnc_register(void); +int eap_peer_pwd_register(void); #endif /* EAP_METHODS_H */ diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index cd410d9..321e9f7 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -23,6 +23,7 @@ #include "common.h" #include "crypto/ms_funcs.h" +#include "crypto/random.h" #include "common/wpa_ctrl.h" #include "mschapv2.h" #include "eap_i.h" @@ -199,7 +200,7 @@ static struct wpabuf * eap_mschapv2_challenge_reply( "in Phase 1"); peer_challenge = data->peer_challenge; os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); - } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) { + } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { wpabuf_free(resp); return NULL; } @@ -564,7 +565,7 @@ static struct wpabuf * eap_mschapv2_change_password( } /* Peer-Challenge */ - if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; /* Reserved, must be zero */ diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index 2e04831..d42a7f8 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_common/eap_pax_common.h" #include "eap_i.h" @@ -174,7 +175,7 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, pos, left); } - if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); ret->ignore = TRUE; return NULL; diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 2b72084..7cb8213 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -196,7 +196,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) * @nak_type: TLV type (EAP_TLV_*) * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure * - * This funtion builds an EAP-TLV NAK message. The caller is responsible for + * This function builds an EAP-TLV NAK message. The caller is responsible for * freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) @@ -285,8 +285,10 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -346,8 +348,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure * - * This funtion builds an EAP-TLV Result message. The caller is responsible for - * freeing the returned buffer. + * This function builds an EAP-TLV Result message. The caller is responsible + * for freeing the returned buffer. */ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, struct eap_peap_data *data, @@ -1247,9 +1249,12 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ - peap_prfplus(data->peap_version, data->ipmk, 40, - "Session Key Generating Function", - (u8 *) "\00", 1, csk, sizeof(csk)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index ccf871e..592ef13 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -19,6 +19,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_i.h" @@ -130,7 +131,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); - if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) { + if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); ret->ignore = TRUE; return NULL; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c new file mode 100644 index 0000000..6511a66 --- /dev/null +++ b/src/eap_peer/eap_pwd.c @@ -0,0 +1,763 @@ +/* + * EAP peer method: EAP-pwd (RFC 5931) + * Copyright (c) 2010, Dan Harkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD license. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u16 group_num; + EAP_PWD_group *grp; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *server_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *server_element; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-UNK"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_INFO, "EAP-PWD: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); + return NULL; + } + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); + return NULL; + } + + if ((data = os_zalloc(sizeof(*data))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); + return NULL; + } + + if ((data->bnctx = BN_CTX_new()) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data); + return NULL; + } + + if ((data->id_peer = os_malloc(identity_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + BN_CTX_free(data->bnctx); + os_free(data); + return NULL; + } + + os_memcpy(data->id_peer, identity, identity_len); + data->id_peer_len = identity_len; + + if ((data->password = os_malloc(password_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); + BN_CTX_free(data->bnctx); + os_free(data->id_peer); + os_free(data); + return NULL; + } + os_memcpy(data->password, password, password_len); + data->password_len = password_len; + + data->state = PWD_ID_Req; + + return data; +} + + +static void eap_pwd_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->server_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->server_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static struct wpabuf * +eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + struct wpabuf *resp; + + if (data->state != PWD_ID_Req) { + ret->ignore = TRUE; + return NULL; + } + + if (payload_len < sizeof(struct eap_pwd_id)) { + ret->ignore = TRUE; + return NULL; + } + + id = (struct eap_pwd_id *) payload; + data->group_num = be_to_host16(id->group_num); + if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said group %d", + data->group_num); + + data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_server == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + return NULL; + } + data->id_server_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_server, id->identity, data->id_server_len); + wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", + data->id_server, data->id_server_len); + + if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) == + NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + return NULL; + } + + /* compute PWE */ + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); + return NULL; + } + + wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + 1 + sizeof(struct eap_pwd_id) + data->id_peer_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH); + wpabuf_put_be16(resp, data->group_num); + wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(resp, id->token, sizeof(id->token)); + wpabuf_put_u8(resp, EAP_PWD_PREP_NONE); + wpabuf_put_data(resp, data->id_peer, data->id_peer_len); + + eap_pwd_state(data, PWD_Commit_Req); + + return resp; +} + + +static struct wpabuf * +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct wpabuf *resp = NULL; + EC_POINT *K = NULL, *point = NULL; + BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; + u16 offset; + u8 *ptr, *scalar = NULL, *element = NULL; + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " + "for curve"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); + goto fin; + } + + /* process the request */ + if (((data->server_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " + "fail"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure server's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->server_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "server element by order!\n"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->server_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->server_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "shared key point by order"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " + "infinity!\n"); + goto fin; + } + + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " + "shared secret from point"); + goto fin; + } + + /* now do the response */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + sizeof(struct eap_pwd_hdr) + + BN_num_bytes(data->grp->order) + + (2 * BN_num_bytes(data->grp->prime)), + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + goto fin; + + wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH); + + /* we send the element as (x,y) follwed by the scalar */ + wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime))); + wpabuf_put_data(resp, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + BN_free(cofactor); + EC_POINT_free(K); + EC_POINT_free(point); + if (resp == NULL) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, PWD_Confirm_Req); + + return resp; +} + + +static struct wpabuf * +eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct wpabuf *resp = NULL; + BIGNUM *x = NULL, *y = NULL; + HMAC_CTX ctx; + u32 cs; + u16 grp; + u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + int offset; + + /* + * first build up the ciphersuite which is group | random_function | + * prf + */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " + "fail"); + goto fin; + } + + /* + * server's commit is H(k | server_element | server_scalar | + * peer_element | peer_scalar | ciphersuite) + */ + H_Init(&ctx); + + /* + * zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->server_scalar); + BN_bn2bin(data->server_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* my element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* my scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + + /* random function fin */ + H_Final(&ctx, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); + + /* + * compute confirm: + * H(k | peer_element | peer_scalar | server_element | server_scalar | + * ciphersuite) + */ + H_Init(&ctx); + + /* k */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* my element */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* my scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->server_scalar); + BN_bn2bin(data->server_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + + /* all done */ + H_Final(&ctx, conf); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + goto fin; + + wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH); + wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH); + + if (compute_keys(data->grp, data->bnctx, data->k, + data->my_scalar, data->server_scalar, conf, ptr, + &cs, data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " + "EMSK"); + goto fin; + } + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + ret->methodState = METHOD_DONE; + if (resp == NULL) { + ret->decision = DECISION_FAIL; + eap_pwd_state(data, FAILURE); + } else { + ret->decision = DECISION_UNCOND_SUCC; + eap_pwd_state(data, SUCCESS); + } + + return resp; +} + + +static struct wpabuf * +eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_pwd_data *data = priv; + struct wpabuf *resp = NULL; + const u8 *pos; + size_t len; + u8 exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); + if ((pos == NULL) || (len < 1)) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + exch = *pos & 0x3f; + switch (exch) { + case EAP_PWD_OPCODE_ID_EXCH: + resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData, + pos + 1, len - 1); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData, + pos + 1, len - 1); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, + pos + 1, len - 1); + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " + "opcode %d", exch); + break; + } + + return resp; +} + + +static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) + return NULL; + + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_pwd_register(void) +{ + struct eap_method *eap; + int ret; + + EVP_add_digest(EVP_sha256()); + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->deinit = eap_pwd_deinit; + eap->process = eap_pwd_process; + eap->isKeyAvailable = eap_pwd_key_available; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index bb06bb2..1474b7f 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -223,7 +224,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) { + if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); return NULL; } diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 3d8afb2..2e81850 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -17,6 +17,7 @@ #include "common.h" #include "pcsc_funcs.h" #include "crypto/milenage.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_config.h" #include "eap_common/eap_sim_common.h" @@ -93,7 +94,7 @@ static void * eap_sim_init(struct eap_sm *sm) if (data == NULL) return NULL; - if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); @@ -265,21 +266,20 @@ static int eap_sim_supported_ver(int version) static void eap_sim_clear_identities(struct eap_sim_data *data, int id) { - wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s", - id & CLEAR_PSEUDONYM ? " pseudonym" : "", - id & CLEAR_REAUTH_ID ? " reauth_id" : "", - id & CLEAR_EAP_ID ? " eap_id" : ""); - if (id & CLEAR_PSEUDONYM) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } - if (id & CLEAR_EAP_ID) { + if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; @@ -287,24 +287,44 @@ static void eap_sim_clear_identities(struct eap_sim_data *data, int id) } -static int eap_sim_learn_ids(struct eap_sim_data *data, +static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; return -1; } os_memcpy(data->pseudonym, attr->next_pseudonym, attr->next_pseudonym_len); - data->pseudonym_len = attr->next_pseudonym_len; - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", - data->pseudonym, - data->pseudonym_len); + if (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; } if (attr->next_reauth_id) { @@ -313,6 +333,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -648,11 +669,11 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - /* Old reauthentication and pseudonym identities must not be used - * anymore. In other words, if no new identities are received, full - * authentication will be used on next reauthentication. */ - eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | - CLEAR_EAP_ID); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new reauth identity is received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -663,7 +684,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, return eap_sim_client_error( data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - eap_sim_learn_ids(data, &eattr); + eap_sim_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -861,7 +882,7 @@ static struct wpabuf * eap_sim_process_reauthentication( data->nonce_s, data->mk, data->msk, data->emsk); eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); - eap_sim_learn_ids(data, &eattr); + eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; @@ -995,7 +1016,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); os_free(data); diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 7bd50f6..93df756 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -112,7 +112,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); eap_tls_params_from_conf1(params, config); } - params->tls_ia = data->tls_ia; /* * Use blob data, if available. Otherwise, leave reference to external @@ -361,7 +360,8 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, eap_peer_tls_reset_input(data); return -1; } - wpabuf_put_buf(data->tls_in, in_data); + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index e9e0998..e9a07b8 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -66,11 +66,6 @@ struct eap_ssl_data { int include_tls_length; /** - * tls_ia - Whether TLS/IA is enabled for this TLS connection - */ - int tls_ia; - - /** * eap - EAP state machine allocated with eap_peer_sm_init() */ struct eap_sm *eap; diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 2573780..2c939f9 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -26,17 +26,7 @@ #include "eap_config.h" -/* Maximum supported TTLS version - * 0 = RFC 5281 - * 1 = draft-funk-eap-ttls-v1-00.txt - */ -#ifndef EAP_TTLS_VERSION -#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ -#endif /* EAP_TTLS_VERSION */ - - -#define MSCHAPV2_KEY_LEN 16 -#define MSCHAPV2_NT_RESPONSE_LEN 24 +#define EAP_TTLS_VERSION 0 static void eap_ttls_deinit(struct eap_sm *sm, void *priv); @@ -44,9 +34,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv); struct eap_ttls_data { struct eap_ssl_data ssl; - int ssl_initialized; - int ttls_version, force_ttls_version; + int ttls_version; const struct eap_method *phase2_method; void *phase2_priv; @@ -91,22 +80,9 @@ static void * eap_ttls_init(struct eap_sm *sm) if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; - data->force_ttls_version = -1; selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; -#if EAP_TTLS_VERSION > 0 - if (config && config->phase1) { - const char *pos = os_strstr(config->phase1, "ttlsver="); - if (pos) { - data->force_ttls_version = atoi(pos + 8); - data->ttls_version = data->force_ttls_version; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version " - "%d", data->force_ttls_version); - } - } -#endif /* EAP_TTLS_VERSION */ - if (config && config->phase2) { if (os_strstr(config->phase2, "autheap=")) { selected = "EAP"; @@ -140,19 +116,11 @@ static void * eap_ttls_init(struct eap_sm *sm) data->phase2_eap_type.method = EAP_TYPE_NONE; } -#if EAP_TTLS_VERSION > 0 - if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && - data->ttls_version > 0) { - if (data->force_ttls_version > 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " - "TLS library does not support TLS/IA.", - data->force_ttls_version); - eap_ttls_deinit(sm, data); - return NULL; - } - data->ttls_version = 0; + if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; } -#endif /* EAP_TTLS_VERSION */ return data; } @@ -176,8 +144,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) return; eap_ttls_phase2_eap_deinit(sm, data); os_free(data->phase2_eap_types); - if (data->ssl_initialized) - eap_peer_tls_ssl_deinit(sm, &data->ssl); + eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); wpabuf_free(data->pending_phase2_req); os_free(data); @@ -202,7 +169,7 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -246,39 +213,6 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, } -#if EAP_TTLS_VERSION > 0 -static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *key, size_t key_len) -{ - u8 *buf; - size_t buf_len; - int ret; - - if (key) { - buf_len = 2 + key_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - WPA_PUT_BE16(buf, key_len); - os_memcpy(buf + 2, key, key_len); - } else { - buf = NULL; - buf_len = 0; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " - "secret permutation", buf, buf_len); - ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, - data->ssl.conn, - buf, buf_len); - os_free(buf); - - return ret; -} -#endif /* EAP_TTLS_VERSION */ - - static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { @@ -298,156 +232,10 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static int eap_ttls_v1_derive_key(struct eap_sm *sm, - struct eap_ttls_data *data) -{ - struct tls_keys keys; - u8 *rnd; - - os_free(data->key_data); - data->key_data = NULL; - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive keying " - "material"); - return -1; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - data->key_data = os_malloc(EAP_TLS_KEY_LEN); - if (rnd == NULL || data->key_data == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); - os_free(rnd); - os_free(data->key_data); - data->key_data = NULL; - return -1; - } - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "ttls v1 keying material", rnd, keys.client_random_len + - keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - os_free(rnd); - os_free(data->key_data); - data->key_data = NULL; - return -1; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", - rnd, keys.client_random_len + keys.server_random_len); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", - keys.inner_secret, keys.inner_secret_len); - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", - data->key_data, EAP_TLS_KEY_LEN); - - return 0; -} -#endif /* EAP_TTLS_VERSION */ - - static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { -#if EAP_TTLS_VERSION > 0 - struct tls_keys keys; - u8 *challenge, *rnd; -#endif /* EAP_TTLS_VERSION */ - - if (data->ttls_version == 0) { - return eap_peer_tls_derive_key(sm, &data->ssl, - "ttls challenge", len); - } - -#if EAP_TTLS_VERSION > 0 - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive " - "implicit challenge"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - challenge = os_malloc(len); - if (rnd == NULL || challenge == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " - "challenge derivation"); - os_free(rnd); - os_free(challenge); - return NULL; - } - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "inner application challenge", rnd, - keys.client_random_len + keys.server_random_len, - challenge, len)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " - "challenge"); - os_free(rnd); - os_free(challenge); - return NULL; - } - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", - challenge, len); - - return challenge; - -#else /* EAP_TTLS_VERSION */ - - return NULL; - -#endif /* EAP_TTLS_VERSION */ -} - - -static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm, - struct eap_ttls_data *data, - struct eap_method_ret *ret) -{ -#if EAP_TTLS_VERSION > 0 - if (data->ttls_version > 0) { - const struct eap_method *m = data->phase2_method; - void *priv = data->phase2_priv; - - /* TTLSv1 requires TLS/IA FinalPhaseFinished */ - if (ret->decision == DECISION_UNCOND_SUCC) - ret->decision = DECISION_COND_SUCC; - ret->methodState = METHOD_CONT; - - if (ret->decision == DECISION_COND_SUCC && - m->isKeyAvailable && m->getKey && - m->isKeyAvailable(sm, priv)) { - u8 *key; - size_t key_len; - key = m->getKey(sm, priv, &key_len); - if (key) { - eap_ttls_ia_permute_inner_secret( - sm, data, key, key_len); - os_free(key); - } - } - } -#endif /* EAP_TTLS_VERSION */ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } @@ -494,7 +282,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm, ret->methodState = iret.methodState; ret->decision = iret.decision; } - eap_ttlsv1_phase2_eap_finish(sm, data, ret); return 0; } @@ -615,26 +402,6 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, } -static void eap_ttlsv1_permute_inner(struct eap_sm *sm, - struct eap_ttls_data *data) -{ -#if EAP_TTLS_VERSION > 0 - u8 session_key[2 * MSCHAPV2_KEY_LEN]; - - if (data->ttls_version == 0) - return; - - get_asymetric_start_key(data->master_key, session_key, - MSCHAPV2_KEY_LEN, 0, 0); - get_asymetric_start_key(data->master_key, - session_key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 0); - eap_ttls_ia_permute_inner_secret(sm, data, session_key, - sizeof(session_key)); -#endif /* EAP_TTLS_VERSION */ -} - - static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, @@ -674,7 +441,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, "implicit challenge"); return -1; } - peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, @@ -687,7 +453,14 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ - os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " + "random data for peer challenge"); + return -1; + } + peer_challenge = pos; pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; @@ -695,6 +468,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, password_len, pwhash, challenge, peer_challenge, pos, data->auth_response, data->master_key)) { + os_free(challenge); wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); @@ -702,8 +476,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, } data->auth_response_valid = 1; - eap_ttlsv1_permute_inner(sm, data); - pos += 24; os_free(challenge); AVP_PAD(buf, pos); @@ -711,7 +483,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (sm->workaround && data->ttls_version == 0) { + if (sm->workaround) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ @@ -798,17 +570,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/MSCHAP does not provide tunneled success - * notification, so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/MSCHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -859,17 +624,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/PAP does not provide tunneled success notification, - * so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/PAP does not provide tunneled success notification, + * so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -942,17 +700,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (data->ttls_version > 0) { - /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success, - * so do not allow connection to be terminated yet. */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - /* EAP-TTLS/CHAP does not provide tunneled success - * notification, so assume that Phase2 succeeds. */ - ret->methodState = METHOD_DONE; - ret->decision = DECISION_COND_SUCC; - } + /* EAP-TTLS/CHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; return 0; } @@ -1027,36 +778,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static struct wpabuf * eap_ttls_build_phase_finished( - struct eap_sm *sm, struct eap_ttls_data *data, int id, int final) -{ - struct wpabuf *req, *buf; - - buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, - final); - if (buf == NULL) - return NULL; - - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, - 1 + wpabuf_len(buf), - EAP_CODE_RESPONSE, id); - if (req == NULL) { - wpabuf_free(buf); - return NULL; - } - - wpabuf_put_u8(req, data->ttls_version); - wpabuf_put_buf(req, buf); - wpabuf_free(buf); - eap_update_len(req); - - return req; -} -#endif /* EAP_TTLS_VERSION */ - - struct ttls_parse_avp { u8 *mschapv2; u8 *eapdata; @@ -1366,19 +1087,9 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " "authentication succeeded"); - if (data->ttls_version > 0) { - /* - * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report - * success, so do not allow connection to be terminated - * yet. - */ - ret->methodState = METHOD_CONT; - ret->decision = DECISION_COND_SUCC; - } else { - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; - data->phase2_success = 1; - } + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; /* * Reply with empty data; authentication server will reply @@ -1493,24 +1204,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, } -#if EAP_TTLS_VERSION > 0 -static void eap_ttls_final_phase_finished(struct eap_sm *sm, - struct eap_ttls_data *data, - struct eap_method_ret *ret, - u8 identifier, - struct wpabuf **out_data) -{ - wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received"); - wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; - data->phase2_success = 1; - *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1); - eap_ttls_v1_derive_key(sm, data); -} -#endif /* EAP_TTLS_VERSION */ - - static int eap_ttls_implicit_identity_request(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, @@ -1534,6 +1227,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, "processing failed"); retval = -1; } else { + struct eap_peer_config *config = eap_get_config(sm); + if (resp == NULL && + (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password)) { + /* + * Use empty buffer to force implicit request + * processing when EAP request is re-processed after + * user input. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc(0); + } + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, out_data); } @@ -1627,17 +1335,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, if (retval) goto done; -#if EAP_TTLS_VERSION > 0 - if (data->ttls_version > 0 && - (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) && - tls_connection_ia_final_phase_finished(sm->ssl_ctx, - data->ssl.conn)) { - eap_ttls_final_phase_finished(sm, data, ret, identifier, - out_data); - goto done; - } -#endif /* EAP_TTLS_VERSION */ - continue_req: data->phase2_start = 0; @@ -1662,46 +1359,6 @@ done: } -static int eap_ttls_process_start(struct eap_sm *sm, - struct eap_ttls_data *data, u8 flags, - struct eap_method_ret *ret) -{ - struct eap_peer_config *config = eap_get_config(sm); - - wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)", - flags & EAP_TLS_VERSION_MASK, data->ttls_version); -#if EAP_TTLS_VERSION > 0 - if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version) - data->ttls_version = flags & EAP_TLS_VERSION_MASK; - if (data->force_ttls_version >= 0 && - data->force_ttls_version != data->ttls_version) { - wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " - "forced TTLS version %d", - data->force_ttls_version); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - ret->allowNotifications = FALSE; - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d", - data->ttls_version); - - if (data->ttls_version > 0) - data->ssl.tls_ia = 1; -#endif /* EAP_TTLS_VERSION */ - if (!data->ssl_initialized && - eap_peer_tls_ssl_init(sm, &data->ssl, config)) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); - return -1; - } - data->ssl_initialized = 1; - - wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); - - return 0; -} - - static int eap_ttls_process_handshake(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret, @@ -1725,8 +1382,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, ret->methodState = METHOD_MAY_CONT; } data->phase2_start = 1; - if (data->ttls_version == 0) - eap_ttls_v0_derive_key(sm, data); + eap_ttls_v0_derive_key(sm, data); if (*out_data == NULL || wpabuf_len(*out_data) == 0) { if (eap_ttls_decrypt(sm, data, ret, identifier, @@ -1761,7 +1417,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret) { - if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) { + if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; if (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC) { @@ -1779,8 +1435,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, } #endif /* EAP_TNC */ } - } else if (data->ttls_version == 0 && - ret->methodState == METHOD_MAY_CONT && + } else if (ret->methodState == METHOD_MAY_CONT && (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " @@ -1808,8 +1463,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, id = eap_get_id(reqData); if (flags & EAP_TLS_FLAGS_START) { - if (eap_ttls_process_start(sm, data, flags, ret) < 0) - return NULL; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->ttls_version); /* RFC 5281, Ch. 9.2: * "This packet MAY contain additional information in the form @@ -1817,13 +1473,6 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, * For now, ignore any potential extra data. */ left = 0; - } else if (!data->ssl_initialized) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not " - "include Start flag"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_FAIL; - ret->allowNotifications = FALSE; - return NULL; } resp = NULL; diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 8317f72..09d8a1c 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -203,6 +203,10 @@ static void * eap_wsc_init(struct eap_sm *sm) return NULL; } + pos = os_strstr(phase1, "dev_pw_id="); + if (pos && cfg.pin) + cfg.dev_pw_id = atoi(pos + 10); + res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); if (res < 0) { os_free(data); @@ -219,10 +223,16 @@ static void * eap_wsc_init(struct eap_sm *sm) os_free(data); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + res = eap_get_config_fragment_size(sm); + if (res > 0) + data->fragment_size = res; + else + data->fragment_size = WSC_FRAGMENT_SIZE; + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", + (unsigned int) data->fragment_size); if (registrar && cfg.pin) { - wps_registrar_add_pin(data->wps_ctx->registrar, NULL, + wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, cfg.pin, cfg.pin_len, 0); } diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index 309a331..acd7611 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -16,6 +16,7 @@ #include "common.h" #include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -424,7 +425,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (kei_len - 4 != data->dh->prime_len) { @@ -1133,7 +1134,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) data->r_spi, IKEV2_SPI_LEN); data->r_nonce_len = IKEV2_NONCE_MIN_LEN; - if (os_get_random(data->r_nonce, data->r_nonce_len)) + if (random_get_bytes(data->r_nonce, data->r_nonce_len)) return NULL; #ifdef CCNS_PL /* Zeros are removed incorrectly from the beginning of the nonces in diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index eaaa168..a70d70c 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -180,11 +180,11 @@ TNC_Result TNC_TNCC_ReportMessageTypes( imc = tnc_imc[imcID]; os_free(imc->supported_types); imc->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imc->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 92400a5..e1f500a 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -95,6 +95,7 @@ struct eap_config { void *eap_sim_db_priv; Boolean backend_auth; int eap_server; + u16 pwd_group; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -106,7 +107,11 @@ struct eap_config { int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; + const struct wpabuf *assoc_p2p_ie; const u8 *peer_addr; + int fragment_size; + + int pbc_in_m1; }; @@ -120,5 +125,6 @@ void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); +void eap_server_clear_identity(struct eap_sm *sm); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 4269a8c..f48cf71 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -119,7 +119,7 @@ struct eap_sm { /* Full authenticator state machine local variables */ - /* Long-term (maintained betwen packets) */ + /* Long-term (maintained between packets) */ EapType currentMethod; int currentId; enum { @@ -181,12 +181,19 @@ struct eap_sm { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + u16 pwd_group; struct wps_context *wps; struct wpabuf *assoc_wps_ie; + struct wpabuf *assoc_p2p_ie; Boolean start_reauth; u8 peer_addr[ETH_ALEN]; + + /* Fragmentation size for EAP method init() handler */ + int fragment_size; + + int pbc_in_m1; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index 5d4d92c..4a5296e 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -49,5 +49,6 @@ int eap_server_fast_register(void); int eap_server_wsc_register(void); int eap_server_ikev2_register(void); int eap_server_tnc_register(void); +int eap_server_pwd_register(void); #endif /* EAP_SERVER_METHODS_H */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index fdc26f9..7a5beb6 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -136,6 +136,14 @@ SM_STATE(EAP, INITIALIZE) { SM_ENTRY(EAP, INITIALIZE); + if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) { + /* + * Need to allow internal Identity method to be used instead + * of passthrough at the beginning of reauthentication. + */ + eap_server_clear_identity(sm); + } + sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; @@ -1028,9 +1036,12 @@ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) not_found: /* not found - remove from the list */ - os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - (EAP_MAX_METHODS - i - 1) * - sizeof(sm->user->methods[0])); + if (i + 1 < EAP_MAX_METHODS) { + os_memmove(&sm->user->methods[i], + &sm->user->methods[i + 1], + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); + } sm->user->methods[EAP_MAX_METHODS - 1].vendor = EAP_VENDOR_IETF; sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; @@ -1255,8 +1266,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->wps = conf->wps; if (conf->assoc_wps_ie) sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + if (conf->assoc_p2p_ie) + sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie); if (conf->peer_addr) os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); + sm->fragment_size = conf->fragment_size; + sm->pwd_group = conf->pwd_group; + sm->pbc_in_m1 = conf->pbc_in_m1; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); @@ -1291,6 +1307,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) os_free(sm->eap_if.aaaEapKeyData); eap_user_free(sm->user); wpabuf_free(sm->assoc_wps_ie); + wpabuf_free(sm->assoc_p2p_ie); os_free(sm); } @@ -1362,3 +1379,18 @@ struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) { return &sm->eap_if; } + + +/** + * eap_server_clear_identity - Clear EAP identity information + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function can be used to clear the EAP identity information in the EAP + * server context. This allows the EAP/Identity method to be used again after + * EAPOL-Start or EAPOL-Logoff. + */ +void eap_server_clear_identity(struct eap_sm *sm) +{ + os_free(sm->identity); + sm->identity = NULL; +} diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 4e7db48..6ae7a6f 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -17,6 +17,7 @@ #include "common.h" #include "crypto/sha256.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_i.h" #include "eap_server/eap_sim_db.h" @@ -132,14 +133,13 @@ static void * eap_aka_prime_init(struct eap_sm *sm) return NULL; data->eap_method = EAP_TYPE_AKA_PRIME; - data->network_name = os_malloc(os_strlen(network_name)); + data->network_name = (u8 *) os_strdup(network_name); if (data->network_name == NULL) { os_free(data); return NULL; } data->network_name_len = os_strlen(network_name); - os_memcpy(data->network_name, network_name, data->network_name_len); data->state = IDENTITY; eap_aka_determine_identity(sm, data, 1, 0); @@ -298,8 +298,13 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } os_free(data->next_reauth_id); if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = @@ -440,7 +445,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); @@ -1023,11 +1028,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm, identity_len = id2_len; } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 39beb33..ba17e98 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -18,6 +18,7 @@ #include "crypto/aes_wrap.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_fast_common.h" #include "eap_i.h" @@ -642,7 +643,7 @@ static struct wpabuf * eap_fast_build_crypto_binding( binding->version = EAP_FAST_VERSION; binding->received_version = data->peer_version; binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; - if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) { + if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) { wpabuf_free(buf); return NULL; } @@ -692,7 +693,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, struct eap_tlv_result_tlv *result; struct os_time now; - if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || + if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || os_get_time(&now) < 0) return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key", diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index d0c7559..a794806 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -120,7 +121,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); - if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) { + if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); eap_gpsk_state(data, FAILURE); return NULL; diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 06074ee..ec4fa87 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -93,7 +93,8 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = MSG; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : + IKEV2_FRAGMENT_SIZE; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c index dee2dc5..d03ec53 100644 --- a/src/eap_server/eap_server_md5.c +++ b/src/eap_server/eap_server_md5.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_i.h" #include "eap_common/chap.h" @@ -52,7 +53,7 @@ static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id) struct eap_md5_data *data = priv; struct wpabuf *req; - if (os_get_random(data->challenge, CHALLENGE_LEN)) { + if (random_get_bytes(data->challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c index 900a5dd..4d241a4 100644 --- a/src/eap_server/eap_server_methods.c +++ b/src/eap_server/eap_server_methods.c @@ -167,6 +167,8 @@ void eap_server_unregister_methods(void) const char * eap_server_get_name(int vendor, EapType type) { struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 39d1c6e..64120a4 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -16,6 +16,7 @@ #include "common.h" #include "crypto/ms_funcs.h" +#include "crypto/random.h" #include "eap_i.h" @@ -109,7 +110,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( size_t ms_len; if (!data->auth_challenge_from_tls && - os_get_random(data->auth_challenge, CHALLENGE_LEN)) { + random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " "data"); data->state = FAILURE; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 1dc023b..4d64269 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pax_common.h" @@ -82,7 +83,7 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); - if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 674ecd2..381c44a 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -17,6 +17,7 @@ #include "common.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_common/eap_tlv_common.h" @@ -350,8 +351,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) { + os_free(tk); + return -1; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -414,7 +419,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, #endif /* EAP_SERVER_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || - os_get_random(data->binding_nonce, 32)) { + random_get_bytes(data->binding_nonce, 32)) { wpabuf_free(buf); return NULL; } @@ -1319,9 +1324,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * termination for this label while the one used for deriving * IPMK|CMK did not use null termination. */ - peap_prfplus(data->peap_version, data->ipmk, 40, - "Session Key Generating Function", - (u8 *) "\00", 1, csk, sizeof(csk)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) + return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); eapKeyData = os_malloc(EAP_TLS_KEY_LEN); if (eapKeyData) { diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 4c30346..fb299ae 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -19,6 +19,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "eap_common/eap_psk_common.h" #include "eap_server/eap_i.h" @@ -66,7 +67,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); - if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); data->state = FAILURE; return NULL; @@ -124,8 +125,10 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, os_memcpy(buf, data->id_s, data->id_s_len); os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); - if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) + if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { + os_free(buf); goto fail; + } os_free(buf); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c new file mode 100644 index 0000000..cf714c5 --- /dev/null +++ b/src/eap_server/eap_server_pwd.c @@ -0,0 +1,864 @@ +/* + * hostapd / EAP-pwd (RFC 5931) server + * Copyright (c) 2010, Dan Harkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the BSD license. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u32 token; + u16 group_num; + EAP_PWD_group *grp; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *peer_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *peer_element; + + u8 my_confirm[SHA256_DIGEST_LENGTH]; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-Unk"; + } +} + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->group_num = sm->pwd_group; + wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d", + data->group_num); + data->state = PWD_ID_Req; + + data->id_server = (u8 *) os_strdup("server"); + if (data->id_server) + data->id_server_len = os_strlen((char *) data->id_server); + + data->password = os_malloc(sm->user->password_len); + if (data->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password " + "fail"); + os_free(data->id_server); + os_free(data); + return NULL; + } + data->password_len = sm->user->password_len; + os_memcpy(data->password, sm->user->password, data->password_len); + + data->bnctx = BN_CTX_new(); + if (data->bnctx == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data->password); + os_free(data->id_server); + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_pwd_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->peer_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->peer_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static struct wpabuf * +eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +{ + struct wpabuf *req; + + wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request"); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + sizeof(struct eap_pwd_hdr) + + sizeof(struct eap_pwd_id) + data->id_server_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + + /* an lfsr is good enough to generate unpredictable tokens */ + data->token = os_random(); + wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); + wpabuf_put_be16(req, data->group_num); + wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(req, &data->token, sizeof(data->token)); + wpabuf_put_u8(req, EAP_PWD_PREP_NONE); + wpabuf_put_data(req, data->id_server, data->id_server_len); + + return req; +} + + +static struct wpabuf * +eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +{ + struct wpabuf *req = NULL; + BIGNUM *mask = NULL, *x = NULL, *y = NULL; + u8 *scalar = NULL, *element = NULL; + u16 offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " + "fail"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " + "fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " + "fail"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + sizeof(struct eap_pwd_hdr) + + (2 * BN_num_bytes(data->grp->prime)) + + BN_num_bytes(data->grp->order), + EAP_CODE_REQUEST, id); + if (req == NULL) + goto fin; + wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); + + /* We send the element as (x,y) followed by the scalar */ + wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime))); + wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + if (req == NULL) + eap_pwd_state(data, FAILURE); + + return req; +} + + +static struct wpabuf * +eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +{ + struct wpabuf *req = NULL; + BIGNUM *x = NULL, *y = NULL; + HMAC_CTX ctx; + u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + u16 grp; + int offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); + + /* Each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " + "fail"); + goto fin; + } + + /* + * commit is H(k | server_element | server_scalar | peer_element | + * peer_scalar | ciphersuite) + */ + H_Init(&ctx); + + /* + * Zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + * + * First is k + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* peer element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* peer scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->peer_scalar); + BN_bn2bin(data->peer_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + grp = htons(data->group_num); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + ptr = cruft; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + ptr += sizeof(u8); + H_Update(&ctx, cruft, ptr-cruft); + + /* all done with the random function */ + H_Final(&ctx, conf); + os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH); + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH, + EAP_CODE_REQUEST, id); + if (req == NULL) + goto fin; + + wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); + wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + if (req == NULL) + eap_pwd_state(data, FAILURE); + + return req; +} + + +static struct wpabuf * +eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_pwd_data *data = priv; + + switch (data->state) { + case PWD_ID_Req: + return eap_pwd_build_id_req(sm, data, id); + case PWD_Commit_Req: + return eap_pwd_build_commit_req(sm, data, id); + case PWD_Confirm_Req: + return eap_pwd_build_confirm_req(sm, data, id); + default: + wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", + data->state); + break; + } + + return NULL; +} + + +static Boolean eap_pwd_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos); + + if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH) + return FALSE; + + if (data->state == PWD_Commit_Req && + *pos == EAP_PWD_OPCODE_COMMIT_EXCH) + return FALSE; + + if (data->state == PWD_Confirm_Req && + *pos == EAP_PWD_OPCODE_CONFIRM_EXCH) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d", + *pos, data->state); + + return TRUE; +} + + +static void eap_pwd_process_id_resp(struct eap_sm *sm, + struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + + if (payload_len < sizeof(struct eap_pwd_id)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); + return; + } + + id = (struct eap_pwd_id *) payload; + if ((data->group_num != be_to_host16(id->group_num)) || + (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_peer == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + return; + } + data->id_peer_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_peer, id->identity, data->id_peer_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", + data->id_peer, data->id_peer_len); + + if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + return; + } + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " + "PWE"); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + u8 *ptr; + BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; + EC_POINT *K = NULL, *point = NULL; + int res = 0; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); + + if (((data->peer_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " + "fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " + "cofactor for curve"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure peer's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->peer_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply peer element by order"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->peer_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->peer_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply shared key point by order!\n"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " + "at infinity"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " + "shared secret from secret point"); + goto fin; + } + res = 1; + +fin: + EC_POINT_free(K); + EC_POINT_free(point); + BN_free(cofactor); + BN_free(x); + BN_free(y); + + if (res) + eap_pwd_state(data, PWD_Confirm_Req); + else + eap_pwd_state(data, FAILURE); +} + + +static void +eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + BIGNUM *x = NULL, *y = NULL; + HMAC_CTX ctx; + u32 cs; + u16 grp; + u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + int offset; + + /* build up the ciphersuite: group | random_function | prf */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); + goto fin; + } + + /* + * commit is H(k | peer_element | peer_scalar | server_element | + * server_scalar | ciphersuite) + */ + H_Init(&ctx); + + /* k */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* peer element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* peer scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->peer_scalar); + BN_bn2bin(data->peer_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + H_Update(&ctx, (u8 *)&cs, sizeof(u32)); + + /* all done */ + H_Final(&ctx, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " + "verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); + if (compute_keys(data->grp, data->bnctx, data->k, + data->peer_scalar, data->my_scalar, conf, + data->my_confirm, &cs, data->msk, data->emsk) < 0) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, SUCCESS); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); +} + + +static void eap_pwd_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + u8 exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d", + (pos == NULL) ? "is NULL" : "is not NULL", + (int) len); + return; + } + + exch = *pos & 0x3f; + switch (exch) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_process_id_resp(sm, data, pos + 1, len - 1); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1); + break; + } +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return (data->state == SUCCESS) || (data->state == FAILURE); +} + + +int eap_server_pwd_register(void) +{ + struct eap_method *eap; + int ret; + struct timeval tp; + struct timezone tz; + u32 sr; + + EVP_add_digest(EVP_sha256()); + + sr = 0xdeaddada; + (void) gettimeofday(&tp, &tz); + sr ^= (tp.tv_sec ^ tp.tv_usec); + srandom(sr); + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, + "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->reset = eap_pwd_reset; + eap->buildReq = eap_pwd_build_req; + eap->check = eap_pwd_check; + eap->process = eap_pwd_process; + eap->isDone = eap_pwd_is_done; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + eap->isSuccess = eap_pwd_is_success; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} + diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index ce4848f..a9b515f 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -166,7 +167,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); - if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 436c655..6de2e70 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" @@ -136,8 +137,13 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } os_free(data->next_reauth_id); if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = @@ -232,7 +238,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) return NULL; wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", data->nonce_s, EAP_SIM_NONCE_S_LEN); @@ -621,11 +627,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm, identity_len = id2_len; } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, identity_len, data->next_reauth_id, diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 25ae683..e149ee3 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -45,8 +45,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - /* TODO: make this configurable */ - data->tls_out_limit = 1398; + data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398; if (data->phase2) { /* Limit the fragment size in the inner TLS authentication * since the outer authentication with EAP-PEAP does not yet diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c index f3b70ed..a2d6f17 100644 --- a/src/eap_server/eap_server_tnc.c +++ b/src/eap_server/eap_server_tnc.c @@ -91,7 +91,8 @@ static void * eap_tnc_init(struct eap_sm *sm) return NULL; } - data->fragment_size = 1300; + data->fragment_size = sm->fragment_size > 100 ? + sm->fragment_size - 98 : 1300; return data; } diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 702c50c..398d0f1 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -24,16 +24,7 @@ #include "eap_common/eap_ttls.h" -/* Maximum supported TTLS version - * 0 = RFC 5281 - * 1 = draft-funk-eap-ttls-v1-00.txt - */ -#ifndef EAP_TTLS_VERSION -#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ -#endif /* EAP_TTLS_VERSION */ - - -#define MSCHAPV2_KEY_LEN 16 +#define EAP_TTLS_VERSION 0 static void eap_ttls_reset(struct eap_sm *sm, void *priv); @@ -43,17 +34,15 @@ struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE + PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE } state; int ttls_version; - int force_version; const struct eap_method *phase2_method; void *phase2_priv; int mschapv2_resp_ok; u8 mschapv2_auth_response[20]; u8 mschapv2_ident; - int tls_ia_configured; struct wpabuf *pending_phase2_eap_resp; int tnc_started; }; @@ -72,8 +61,6 @@ static const char * eap_ttls_state_txt(int state) return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; - case PHASE_FINISHED: - return "PHASE_FINISHED"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -111,7 +98,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + ((u32) (hdrlen + len))); return avphdr + hdrlen; } @@ -320,54 +308,8 @@ fail: static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { - struct tls_keys keys; - u8 *challenge, *rnd; - - if (data->ttls_version == 0) { - return eap_server_tls_derive_key(sm, &data->ssl, - "ttls challenge", len); - } - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive " - "implicit challenge"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - challenge = os_malloc(len); - if (rnd == NULL || challenge == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " - "challenge derivation"); - os_free(rnd); - os_free(challenge); - return NULL; - } - os_memcpy(rnd, keys.server_random, keys.server_random_len); - os_memcpy(rnd + keys.server_random_len, keys.client_random, - keys.client_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "inner application challenge", rnd, - keys.client_random_len + keys.server_random_len, - challenge, len)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit " - "challenge"); - os_free(rnd); - os_free(challenge); - return NULL; - } - - os_free(rnd); - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", - challenge, len); - - return challenge; + return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); } @@ -379,27 +321,8 @@ static void * eap_ttls_init(struct eap_sm *sm) if (data == NULL) return NULL; data->ttls_version = EAP_TTLS_VERSION; - data->force_version = -1; - if (sm->user && sm->user->force_version >= 0) { - data->force_version = sm->user->force_version; - wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d", - data->force_version); - data->ttls_version = data->force_version; - } data->state = START; - if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && - data->ttls_version > 0) { - if (data->force_version > 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " - "TLS library does not support TLS/IA.", - data->force_version); - eap_ttls_reset(sm, data); - return NULL; - } - data->ttls_version = 0; - } - if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); @@ -516,14 +439,6 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( } -static struct wpabuf * eap_ttls_build_phase_finished( - struct eap_sm *sm, struct eap_ttls_data *data, int final) -{ - return tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, final); -} - - static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; @@ -559,11 +474,6 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) data->ssl.tls_out_pos = 0; data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); break; - case PHASE_FINISHED: - wpabuf_free(data->ssl.tls_out); - data->ssl.tls_out_pos = 0; - data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1); - break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); @@ -591,37 +501,6 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, } -static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *key, size_t key_len) -{ - u8 *buf; - size_t buf_len; - int ret; - - if (key) { - buf_len = 2 + key_len; - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - WPA_PUT_BE16(buf, key_len); - os_memcpy(buf + 2, key, key_len); - } else { - buf = NULL; - buf_len = 0; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner " - "secret permutation", buf, buf_len); - ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx, - data->ssl.conn, - buf, buf_len); - os_free(buf); - - return ret; -} - - static void eap_ttls_process_phase2_pap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *user_password, @@ -644,8 +523,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } @@ -701,8 +579,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -762,8 +639,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -863,30 +739,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; - if (data->ttls_version > 0) { - const u8 *pw_hash; - u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16]; - u8 session_key[2 * MSCHAPV2_KEY_LEN]; - - if (sm->user->password_hash) - pw_hash = sm->user->password; - else { - nt_password_hash(sm->user->password, - sm->user->password_len, - pw_hash_buf); - pw_hash = pw_hash_buf; - } - hash_nt_password_hash(pw_hash, pw_hash_hash); - get_master_key(pw_hash_hash, nt_response, master_key); - get_asymetric_start_key(master_key, session_key, - MSCHAPV2_KEY_LEN, 0, 0); - get_asymetric_start_key(master_key, - session_key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 0); - eap_ttls_ia_permute_inner_secret(sm, data, - session_key, - sizeof(session_key)); - } if (sm->user->password_hash) { generate_authenticator_response_pwhash( @@ -1030,17 +882,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, } break; case PHASE2_METHOD: - if (data->ttls_version > 0) { - if (m->getKey) { - u8 *key; - size_t key_len; - key = m->getKey(sm, priv, &key_len); - eap_ttls_ia_permute_inner_secret(sm, data, - key, key_len); - } - eap_ttls_state(data, PHASE_FINISHED); - } else - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, SUCCESS); break; case FAILURE: break; @@ -1130,23 +972,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } - if (data->state == PHASE_FINISHED) { - if (wpabuf_len(in_decrypted) == 0 && - tls_connection_ia_final_phase_finished(sm->ssl_ctx, - data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished " - "received"); - eap_ttls_state(data, SUCCESS); - } else { - wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid " - "FinalPhaseFinished"); - eap_ttls_state(data, FAILURE); - } - - wpabuf_free(in_decrypted); - return; - } - wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", in_decrypted); @@ -1245,15 +1070,6 @@ static int eap_ttls_process_version(struct eap_sm *sm, void *priv, data->ttls_version = peer_version; } - if (data->ttls_version > 0 && !data->tls_ia_configured) { - if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable " - "TLS/IA"); - return -1; - } - data->tls_ia_configured = 1; - } - return 0; } @@ -1270,7 +1086,6 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: case PHASE2_METHOD: - case PHASE_FINISHED: eap_ttls_process_phase2(sm, data, data->ssl.tls_in); eap_ttls_start_tnc(sm, data); break; @@ -1279,8 +1094,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); - eap_ttls_state(data, data->ttls_version > 0 ? - PHASE_FINISHED : SUCCESS); + eap_ttls_state(data, SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1321,54 +1135,6 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) } -static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, - struct eap_ttls_data *data) -{ - struct tls_keys keys; - u8 *rnd, *key; - - os_memset(&keys, 0, sizeof(keys)); - if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) || - keys.client_random == NULL || keys.server_random == NULL || - keys.inner_secret == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, " - "client random, or server random to derive keying " - "material"); - return NULL; - } - - rnd = os_malloc(keys.client_random_len + keys.server_random_len); - key = os_malloc(EAP_TLS_KEY_LEN); - if (rnd == NULL || key == NULL) { - wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation"); - os_free(rnd); - os_free(key); - return NULL; - } - os_memcpy(rnd, keys.client_random, keys.client_random_len); - os_memcpy(rnd + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf(keys.inner_secret, keys.inner_secret_len, - "ttls v1 keying material", rnd, keys.client_random_len + - keys.server_random_len, key, EAP_TLS_KEY_LEN)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - os_free(rnd); - os_free(key); - return NULL; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random", - rnd, keys.client_random_len + keys.server_random_len); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret", - keys.inner_secret, keys.inner_secret_len); - - os_free(rnd); - - return key; -} - - static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; @@ -1377,14 +1143,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - if (data->ttls_version == 0) { - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); - } else { - eapKeyData = eap_ttls_v1_derive_key(sm, data); - } - + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 77cf9e2..556882d 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -18,6 +18,7 @@ #include "eloop.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" +#include "p2p/p2p.h" #include "wps/wps.h" @@ -135,14 +136,22 @@ static void * eap_wsc_init(struct eap_sm *sm) } cfg.assoc_wps_ie = sm->assoc_wps_ie; cfg.peer_addr = sm->peer_addr; - if (0 /* TODO: could provide option for forcing PSK format */) - cfg.use_psk_key = 1; +#ifdef CONFIG_P2P + if (sm->assoc_p2p_ie) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P " + "client"); + cfg.use_psk_key = 1; + cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); + } +#endif /* CONFIG_P2P */ + cfg.pbc_in_m1 = sm->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : + WSC_FRAGMENT_SIZE; return data; } diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index aba919a..248b216 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -25,6 +25,7 @@ #include #include "common.h" +#include "crypto/random.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" #include "eloop.h" @@ -830,7 +831,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) char *id, *pos, *end; u8 buf[10]; - if (os_get_random(buf, sizeof(buf))) + if (random_get_bytes(buf, sizeof(buf))) return NULL; id = os_malloc(sizeof(buf) * 2 + 2); if (id == NULL) diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 435ba26..f5bbb14 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -16,6 +16,7 @@ #include "common.h" #include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -403,7 +404,7 @@ static int ikev2_process_ker(struct ikev2_initiator_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (ker_len - 4 != data->dh->prime_len) { @@ -1100,7 +1101,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) data->i_spi, IKEV2_SPI_LEN); data->i_nonce_len = IKEV2_NONCE_MIN_LEN; - if (os_get_random(data->i_nonce, data->i_nonce_len)) + if (random_get_bytes(data->i_nonce, data->i_nonce_len)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); @@ -1148,7 +1149,7 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) if (data->shared_secret == NULL) return NULL; data->shared_secret_len = 16; - if (os_get_random(data->shared_secret, 16)) + if (random_get_bytes(data->shared_secret, 16)) return NULL; } else { os_free(data->shared_secret); diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index 497b51a..637b6f8 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -234,11 +234,11 @@ TNC_Result TNC_TNCS_ReportMessageTypes( return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); imv->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imv->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index a1976e8..e600954 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -762,7 +762,8 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx) + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx) { struct eapol_state_machine *sm; struct eap_config eap_conf; @@ -829,7 +830,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.tnc = eapol->conf.tnc; eap_conf.wps = eapol->conf.wps; eap_conf.assoc_wps_ie = assoc_wps_ie; + eap_conf.assoc_p2p_ie = assoc_p2p_ie; eap_conf.peer_addr = addr; + eap_conf.fragment_size = eapol->conf.fragment_size; + eap_conf.pwd_group = eapol->conf.pwd_group; + eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1012,7 +1017,7 @@ static struct eapol_callbacks eapol_cb = int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) { - if (sm == NULL || ctx != sm->eap) + if (sm == NULL || ctx == NULL || ctx != sm->eap) return -1; eap_sm_pending_cb(sm->eap); @@ -1034,6 +1039,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->msg_ctx = src->msg_ctx; dst->eap_sim_db_priv = src->eap_sim_db_priv; os_free(dst->eap_req_id_text); + dst->pwd_group = src->pwd_group; + dst->pbc_in_m1 = src->pbc_in_m1; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) @@ -1077,6 +1084,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; dst->tnc = src->tnc; dst->wps = src->wps; + dst->fragment_size = src->fragment_size; return 0; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index ef943ad..724bf8b 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -40,6 +40,9 @@ struct eapol_auth_config { int eap_sim_aka_result_ind; int tnc; struct wps_context *wps; + int fragment_size; + u16 pwd_group; + int pbc_in_m1; /* Opaque context pointer to owner data for callback functions */ void *ctx; @@ -79,7 +82,8 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, void eapol_auth_deinit(struct eapol_authenticator *eapol); struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx); + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); void eapol_auth_dump_state(FILE *f, const char *prefix, diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 77cd564..ffc6619 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -561,7 +561,7 @@ SM_STEP(SUPP_BE) * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL * and SUCCESS based on eapFail and eapSuccess, respectively. * However, IEEE Std 802.1X-2004 is also specifying that - * eapNoResp should be set in conjuction with eapSuccess and + * eapNoResp should be set in conjunction with eapSuccess and * eapFail which would mean that more than one of the * transitions here would be activated at the same time. * Skipping RESPONSE and/or RECEIVE states in these cases can @@ -1029,6 +1029,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, } +/** + * eapol_sm_get_method_name - Get EAPOL method name + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * Returns: Static string containing name of current eap method or NULL + */ +const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED || + sm->suppPortStatus != Authorized) + return NULL; + + return eap_sm_get_method_name(sm->eap); +} + + #ifdef CONFIG_CTRL_IFACE /** * eapol_sm_get_status - Get EAPOL state machine status @@ -1798,7 +1813,7 @@ static void eapol_sm_notify_pending(void *ctx) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -static void eapol_sm_eap_param_needed(void *ctx, const char *field, +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, const char *txt) { struct eapol_sm *sm = ctx; @@ -1810,6 +1825,15 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field, #define eapol_sm_eap_param_needed NULL #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct eapol_sm *sm = ctx; + if (sm->ctx->cert_cb) + sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, + cert_hash, cert); +} static struct eapol_callbacks eapol_cb = { @@ -1822,7 +1846,8 @@ static struct eapol_callbacks eapol_cb = eapol_sm_set_config_blob, eapol_sm_get_config_blob, eapol_sm_notify_pending, - eapol_sm_eap_param_needed + eapol_sm_eap_param_needed, + eapol_sm_notify_cert }; @@ -1858,6 +1883,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; conf.wps = ctx->wps; + conf.cert_in_cb = ctx->cert_in_cb; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); if (sm->eap == NULL) { diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1d2a32b..bcb00b5 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -208,10 +208,10 @@ struct eapol_ctx { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: Callback context (ctx) - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); /** @@ -220,6 +220,22 @@ struct eapol_ctx { * @authorized: Whether the supplicant port is now in authorized state */ void (*port_cb)(void *ctx, int authorized); + + /** + * cert_cb - Notification of a peer certificate + * @ctx: Callback context (ctx) + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*cert_cb)(void *ctx, int depth, const char *subject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; }; @@ -255,6 +271,7 @@ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); void eapol_sm_request_reauth(struct eapol_sm *sm); void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); +const char * eapol_sm_get_method_name(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -342,6 +359,10 @@ static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) { } +static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + return NULL; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index 009e02c..e24277c 100644 --- a/src/l2_packet/l2_packet_freebsd.c +++ b/src/l2_packet/l2_packet_freebsd.c @@ -20,7 +20,11 @@ #include #include +#ifdef __sun__ +#include +#else /* __sun__ */ #include +#endif /* __sun__ */ #include #include @@ -139,6 +143,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, } pcap_freecode(&pcap_fp); +#ifndef __sun__ /* * When libpcap uses BPF we must enable "immediate mode" to * receive frames right away; otherwise the system may @@ -153,6 +158,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, /* XXX should we fail? */ } } +#endif /* __sun__ */ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), l2_packet_receive, l2, l2->pcap); @@ -163,6 +169,30 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, static int eth_get(const char *device, u8 ea[ETH_ALEN]) { +#ifdef __sun__ + dlpi_handle_t dh; + u32 physaddrlen = DLPI_PHYSADDR_MAX; + u8 physaddr[DLPI_PHYSADDR_MAX]; + int retval; + + retval = dlpi_open(device, &dh, 0); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_open error: %s", + dlpi_strerror(retval)); + return -1; + } + + retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, + &physaddrlen); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s", + dlpi_strerror(retval)); + dlpi_close(dh); + return -1; + } + os_memcpy(ea, physaddr, ETH_ALEN); + dlpi_close(dh); +#else /* __sun__ */ struct if_msghdr *ifm; struct sockaddr_dl *sdl; u_char *p, *buf; @@ -195,6 +225,7 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN]) errno = ESRCH; return -1; } +#endif /* __sun__ */ return 0; } diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 48d1bde..93e15eb 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -51,7 +51,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, if (l2->l2_hdr) { ret = send(l2->fd, buf, len, 0); if (ret < 0) - perror("l2_packet_send - send"); + wpa_printf(MSG_ERROR, "l2_packet_send - send: %s", + strerror(errno)); } else { struct sockaddr_ll ll; os_memset(&ll, 0, sizeof(ll)); @@ -62,8 +63,10 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, sizeof(ll)); - if (ret < 0) - perror("l2_packet_send - sendto"); + if (ret < 0) { + wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s", + strerror(errno)); + } } return ret; } @@ -82,7 +85,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen); if (res < 0) { - perror("l2_packet_receive - recvfrom"); + wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s", + strerror(errno)); return; } @@ -111,14 +115,16 @@ struct l2_packet_data * l2_packet_init( l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); if (l2->fd < 0) { - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s", + __func__, strerror(errno)); os_free(l2); return NULL; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { - perror("ioctl[SIOCGIFINDEX]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -130,14 +136,16 @@ struct l2_packet_data * l2_packet_init( ll.sll_ifindex = ifr.ifr_ifindex; ll.sll_protocol = htons(protocol); if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("bind[PF_PACKET]"); + wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl[SIOCGIFHWADDR]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -173,14 +181,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "%s: socket: %s", + __func__, strerror(errno)); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { if (errno != EADDRNOTAVAIL) - perror("ioctl[SIOCGIFADDR]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s", + __func__, strerror(errno)); close(s); return -1; } diff --git a/src/p2p/Makefile b/src/p2p/Makefile new file mode 100644 index 0000000..cffba62 --- /dev/null +++ b/src/p2p/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c new file mode 100644 index 0000000..3bcbea6 --- /dev/null +++ b/src/p2p/p2p.c @@ -0,0 +1,3901 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq); +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, + size_t len); +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); + + +/* + * p2p_scan recovery timeout + * + * Many drivers are using 30 second timeout on scan results. Allow a bit larger + * timeout for this to avoid hitting P2P timeout unnecessarily. + */ +#define P2P_SCAN_TIMEOUT 35 + +/** + * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer + * entries will be removed + */ +#define P2P_PEER_EXPIRATION_AGE 300 + +#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) + +static void p2p_expire_peers(struct p2p_data *p2p) +{ + struct p2p_device *dev, *n; + struct os_time now; + size_t i; + + os_get_time(&now); + dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { + if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) + continue; + + if (p2p->cfg->go_connected && + p2p->cfg->go_connected(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr)) { + /* + * We are connected as a client to a group in which the + * peer is the GO, so do not expire the peer entry. + */ + os_get_time(&dev->last_seen); + continue; + } + + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_client_connected( + p2p->groups[i], dev->info.p2p_device_addr)) + break; + } + if (i < p2p->num_groups) { + /* + * The peer is connected as a client in a group where + * we are the GO, so do not expire the peer entry. + */ + os_get_time(&dev->last_seen); + continue; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " + "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } +} + + +static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + p2p_expire_peers(p2p); + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); +} + + +static const char * p2p_state_txt(int state) +{ + switch (state) { + case P2P_IDLE: + return "IDLE"; + case P2P_SEARCH: + return "SEARCH"; + case P2P_CONNECT: + return "CONNECT"; + case P2P_CONNECT_LISTEN: + return "CONNECT_LISTEN"; + case P2P_GO_NEG: + return "GO_NEG"; + case P2P_LISTEN_ONLY: + return "LISTEN_ONLY"; + case P2P_WAIT_PEER_CONNECT: + return "WAIT_PEER_CONNECT"; + case P2P_WAIT_PEER_IDLE: + return "WAIT_PEER_IDLE"; + case P2P_SD_DURING_FIND: + return "SD_DURING_FIND"; + case P2P_PROVISIONING: + return "PROVISIONING"; + case P2P_PD_DURING_FIND: + return "PD_DURING_FIND"; + case P2P_INVITE: + return "INVITE"; + case P2P_INVITE_LISTEN: + return "INVITE_LISTEN"; + case P2P_SEARCH_WHEN_READY: + return "SEARCH_WHEN_READY"; + default: + return "?"; + } +} + + +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev->wps_prov_info; + else + return 0; +} + + +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return; + + dev = p2p_get_device(p2p, addr); + if (dev) + dev->wps_prov_info = 0; +} + + +void p2p_set_state(struct p2p_data *p2p, int new_state) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s", + p2p_state_txt(p2p->state), p2p_state_txt(new_state)); + p2p->state = new_state; +} + + +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Set timeout (state=%s): %u.%06u sec", + p2p_state_txt(p2p->state), sec, usec); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); + eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); +} + + +void p2p_clear_timeout(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)", + p2p_state_txt(p2p->state)); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); +} + + +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status) +{ + struct p2p_go_neg_results res; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + if (p2p->go_neg_peer) + p2p->go_neg_peer->wps_method = WPS_NOT_READY; + p2p->go_neg_peer = NULL; + + os_memset(&res, 0, sizeof(res)); + res.status = status; + if (peer) { + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, + ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, + ETH_ALEN); + } + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_listen_in_find(struct p2p_data *p2p) +{ + unsigned int r, tu; + int freq; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Starting short listen state (state=%s)", + p2p_state_txt(p2p->state)); + + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + return; + } + + os_get_random((u8 *) &r, sizeof(r)); + tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + + p2p->min_disc_int) * 100; + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = 0; + p2p->pending_listen_usec = 1024 * tu; + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, + ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode"); + p2p->pending_listen_freq = 0; + } + wpabuf_free(ies); +} + + +int p2p_listen(struct p2p_data *p2p, unsigned int timeout) +{ + int freq; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Going to listen(only) state"); + + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + return -1; + } + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = timeout / 1000; + p2p->pending_listen_usec = (timeout % 1000) * 1000; + + if (p2p->p2p_scan_running) { + if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: p2p_scan running - connect is already " + "pending - skip listen"); + return 0; + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: p2p_scan running - delay start of listen state"); + p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; + return 0; + } + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return -1; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode"); + p2p->pending_listen_freq = 0; + wpabuf_free(ies); + return -1; + } + wpabuf_free(ies); + + p2p_set_state(p2p, P2P_LISTEN_ONLY); + + return 0; +} + + +static void p2p_device_clear_reported(struct p2p_data *p2p) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_REPORTED; +} + + +/** + * p2p_get_device - Fetch a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address + * @p2p: P2P module context from p2p_init() + * @addr: P2P Interface Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_create_device - Create a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL on failure + * + * If there is already an entry for the peer, it will be returned instead of + * creating a new one. + */ +static struct p2p_device * p2p_create_device(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev, *oldest = NULL; + size_t count = 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + count++; + if (oldest == NULL || + os_time_before(&dev->last_seen, &oldest->last_seen)) + oldest = dev; + } + if (count + 1 > p2p->cfg->max_peers && oldest) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove oldest peer entry to make room for a new " + "peer"); + dl_list_del(&oldest->list); + p2p_device_free(p2p, oldest); + } + + dev = os_zalloc(sizeof(*dev)); + if (dev == NULL) + return NULL; + dl_list_add(&p2p->devices, &dev->list); + os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN); + + return dev; +} + + +static void p2p_copy_client_info(struct p2p_device *dev, + struct p2p_client_info *cli) +{ + os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); + dev->info.device_name[cli->dev_name_len] = '\0'; + dev->info.dev_capab = cli->dev_capab; + dev->info.config_methods = cli->config_methods; + os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); + dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; + os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, + dev->info.wps_sec_dev_type_list_len); +} + + +static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, + const u8 *go_interface_addr, int freq, + const u8 *gi, size_t gi_len) +{ + struct p2p_group_info info; + size_t c; + struct p2p_device *dev; + + if (gi == NULL) + return 0; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return -1; + + /* + * Clear old data for this group; if the devices are still in the + * group, the information will be restored in the loop following this. + */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN) == 0) { + os_memset(dev->member_in_go_iface, 0, ETH_ALEN); + os_memset(dev->member_in_go_dev, 0, ETH_ALEN); + } + } + + for (c = 0; c < info.num_clients; c++) { + struct p2p_client_info *cli = &info.client[c]; + if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr, + ETH_ALEN) == 0) + continue; /* ignore our own entry */ + dev = p2p_get_device(p2p, cli->p2p_device_addr); + if (dev) { + /* + * Update information only if we have not received this + * directly from the client. + */ + if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_PROBE_REQ_ONLY)) + p2p_copy_client_info(dev, cli); + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; + } + } else { + dev = p2p_create_device(p2p, cli->p2p_device_addr); + if (dev == NULL) + continue; + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + p2p_copy_client_info(dev, cli); + dev->oper_freq = freq; + p2p->cfg->dev_found(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + &dev->info, 1); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + os_memcpy(dev->interface_addr, cli->p2p_interface_addr, + ETH_ALEN); + os_get_time(&dev->last_seen); + os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); + os_memcpy(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN); + } + + return 0; +} + + +static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, + const struct p2p_message *msg) +{ + os_memcpy(dev->info.device_name, msg->device_name, + sizeof(dev->info.device_name)); + + if (msg->manufacturer && + msg->manufacturer_len < sizeof(dev->info.manufacturer)) { + os_memset(dev->info.manufacturer, 0, + sizeof(dev->info.manufacturer)); + os_memcpy(dev->info.manufacturer, msg->manufacturer, + msg->manufacturer_len); + } + + if (msg->model_name && + msg->model_name_len < sizeof(dev->info.model_name)) { + os_memset(dev->info.model_name, 0, + sizeof(dev->info.model_name)); + os_memcpy(dev->info.model_name, msg->model_name, + msg->model_name_len); + } + + if (msg->model_number && + msg->model_number_len < sizeof(dev->info.model_number)) { + os_memset(dev->info.model_number, 0, + sizeof(dev->info.model_number)); + os_memcpy(dev->info.model_number, msg->model_number, + msg->model_number_len); + } + + if (msg->serial_number && + msg->serial_number_len < sizeof(dev->info.serial_number)) { + os_memset(dev->info.serial_number, 0, + sizeof(dev->info.serial_number)); + os_memcpy(dev->info.serial_number, msg->serial_number, + msg->serial_number_len); + } + + if (msg->pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type, + sizeof(dev->info.pri_dev_type)); + else if (msg->wps_pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type, + sizeof(dev->info.pri_dev_type)); + + if (msg->wps_sec_dev_type_list) { + os_memcpy(dev->info.wps_sec_dev_type_list, + msg->wps_sec_dev_type_list, + msg->wps_sec_dev_type_list_len); + dev->info.wps_sec_dev_type_list_len = + msg->wps_sec_dev_type_list_len; + } + + if (msg->capability) { + dev->info.dev_capab = msg->capability[0]; + dev->info.group_capab = msg->capability[1]; + } + + if (msg->ext_listen_timing) { + dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing); + dev->ext_listen_interval = + WPA_GET_LE16(msg->ext_listen_timing + 2); + } + + if (!probe_req) { + dev->info.config_methods = msg->config_methods ? + msg->config_methods : msg->wps_config_methods; + } +} + + +/** + * p2p_add_device - Add peer entries based on scan results + * @p2p: P2P module context from p2p_init() + * @addr: Source address of Beacon or Probe Response frame (may be either + * P2P Device Address or P2P Interface Address) + * @level: Signal level (signal strength of the received frame from the peer) + * @freq: Frequency on which the Beacon or Probe Response frame was received + * @ies: IEs from the Beacon or Probe Response frame + * @ies_len: Length of ies buffer in octets + * Returns: 0 on success, -1 on failure + * + * If the scan result is for a GO, the clients in the group will also be added + * to the peer table. This function can also be used with some other frames + * like Provision Discovery Request that contains P2P Capability and P2P Device + * Info attributes. + */ +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, + const u8 *ies, size_t ies_len) +{ + struct p2p_device *dev; + struct p2p_message msg; + const u8 *p2p_dev_addr; + int i; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P IE for a device entry"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore scan data without P2P Device Info or " + "P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (!is_zero_ether_addr(p2p->peer_filter) && + os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer " + "filter for " MACSTR " due to peer filter", + MAC2STR(p2p_dev_addr)); + return 0; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + os_get_time(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + + if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) + os_memcpy(dev->interface_addr, addr, ETH_ALEN); + if (msg.ssid && + (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0)) { + os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]); + dev->oper_ssid_len = msg.ssid[1]; + } + + if (freq >= 2412 && freq <= 2484 && msg.ds_params && + *msg.ds_params >= 1 && *msg.ds_params <= 14) { + int ds_freq; + if (*msg.ds_params == 14) + ds_freq = 2484; + else + ds_freq = 2407 + *msg.ds_params * 5; + if (freq != ds_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Update Listen frequency based on DS " + "Parameter Set IE: %d -> %d MHz", + freq, ds_freq); + freq = ds_freq; + } + } + + if (dev->listen_freq && dev->listen_freq != freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Update Listen frequency based on scan " + "results (" MACSTR " %d -> %d MHz (DS param %d)", + MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, + freq, msg.ds_params ? *msg.ds_params : -1); + } + dev->listen_freq = freq; + if (msg.group_info) + dev->oper_freq = freq; + dev->info.level = level; + + p2p_copy_wps_info(dev, 0, &msg); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (msg.wps_vendor_ext[i] == NULL) + break; + dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy( + msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]); + if (dev->info.wps_vendor_ext[i] == NULL) + break; + } + + p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, + msg.group_info_len); + + p2p_parse_free(&msg); + + if (p2p_pending_sd_req(p2p, dev)) + dev->flags |= P2P_DEV_SD_SCHEDULE; + + if (dev->flags & P2P_DEV_REPORTED) + return 0; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer found with Listen frequency %d MHz", freq); + if (dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not report rejected device"); + return 0; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + + return 0; +} + + +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) +{ + int i; + + if (p2p->go_neg_peer == dev) { + /* + * If GO Negotiation is in progress, report that it has failed. + */ + p2p_go_neg_failed(p2p, dev, -1); + p2p->go_neg_peer = NULL; + } + if (p2p->invite_peer == dev) + p2p->invite_peer = NULL; + if (p2p->sd_peer == dev) + p2p->sd_peer = NULL; + if (p2p->pending_client_disc_go == dev) + p2p->pending_client_disc_go = NULL; + + /* dev_lost() device, but only if it was previously dev_found() */ + if (dev->flags & P2P_DEV_REPORTED_ONCE) + p2p->cfg->dev_lost(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + os_free(dev); +} + + +static int p2p_get_next_prog_freq(struct p2p_data *p2p) +{ + struct p2p_channels *c; + struct p2p_reg_class *cla; + size_t cl, ch; + int found = 0; + u8 reg_class; + u8 channel; + int freq; + + c = &p2p->cfg->channels; + for (cl = 0; cl < c->reg_classes; cl++) { + cla = &c->reg_class[cl]; + if (cla->reg_class != p2p->last_prog_scan_class) + continue; + for (ch = 0; ch < cla->channels; ch++) { + if (cla->channel[ch] == p2p->last_prog_scan_chan) { + found = 1; + break; + } + } + if (found) + break; + } + + if (!found) { + /* Start from beginning */ + reg_class = c->reg_class[0].reg_class; + channel = c->reg_class[0].channel[0]; + } else { + /* Pick the next channel */ + ch++; + if (ch == cla->channels) { + cl++; + if (cl == c->reg_classes) + cl = 0; + ch = 0; + } + reg_class = c->reg_class[cl].reg_class; + channel = c->reg_class[cl].channel[ch]; + } + + freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search " + "channel: reg_class %u channel %u -> %d MHz", + reg_class, channel, freq); + p2p->last_prog_scan_class = reg_class; + p2p->last_prog_scan_chan = channel; + + if (freq == 2412 || freq == 2437 || freq == 2462) + return 0; /* No need to add social channels */ + return freq; +} + + +static void p2p_search(struct p2p_data *p2p) +{ + int freq = 0; + enum p2p_scan_type type; + + if (p2p->drv_in_listen) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " + "in Listen state - wait for it to end before " + "continuing"); + return; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + + if (p2p->go_neg_peer) { + /* + * Only scan the known listen frequency of the peer + * during GO Negotiation start. + */ + freq = p2p->go_neg_peer->listen_freq; + if (freq <= 0) + freq = p2p->go_neg_peer->oper_freq; + type = P2P_SCAN_SPECIFIC; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " + "for freq %u (GO Neg)", freq); + } else if (p2p->invite_peer) { + /* + * Only scan the known listen frequency of the peer + * during Invite start. + */ + freq = p2p->invite_peer->listen_freq; + if (freq <= 0) + freq = p2p->invite_peer->oper_freq; + type = P2P_SCAN_SPECIFIC; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " + "for freq %u (Invite)", freq); + } else if (p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) { + type = P2P_SCAN_SOCIAL_PLUS_ONE; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " + "(+ freq %u)", freq); + } else { + type = P2P_SCAN_SOCIAL; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); + } + + if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Scan request failed"); + p2p_continue_find(p2p); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } +} + + +static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop"); + p2p_stop_find(p2p); +} + + +static int p2p_run_after_scan(struct p2p_data *p2p) +{ + struct p2p_device *dev; + enum p2p_after_scan op; + + if (p2p->after_scan_tx) { + /* TODO: schedule p2p_run_after_scan to be called from TX + * status callback(?) */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending " + "Action frame at p2p_scan completion"); + p2p->cfg->send_action(p2p->cfg->cb_ctx, + p2p->after_scan_tx->freq, + p2p->after_scan_tx->dst, + p2p->after_scan_tx->src, + p2p->after_scan_tx->bssid, + (u8 *) (p2p->after_scan_tx + 1), + p2p->after_scan_tx->len, + p2p->after_scan_tx->wait_time); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + return 1; + } + + op = p2p->start_after_scan; + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + switch (op) { + case P2P_AFTER_SCAN_NOTHING: + break; + case P2P_AFTER_SCAN_LISTEN: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " + "requested Listen state"); + p2p_listen(p2p, p2p->pending_listen_sec * 1000 + + p2p->pending_listen_usec / 1000); + return 1; + case P2P_AFTER_SCAN_CONNECT: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " + "requested connect with " MACSTR, + MAC2STR(p2p->after_scan_peer)); + dev = p2p_get_device(p2p, p2p->after_scan_peer); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not " + "known anymore"); + break; + } + p2p_connect_send(p2p, dev); + return 1; + } + + return 0; +} + + +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + int running; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout " + "(running=%d)", p2p->p2p_scan_running); + running = p2p->p2p_scan_running; + /* Make sure we recover from missed scan results callback */ + p2p->p2p_scan_running = 0; + + if (running) + p2p_run_after_scan(p2p); +} + + +static void p2p_free_req_dev_types(struct p2p_data *p2p) +{ + p2p->num_req_dev_types = 0; + os_free(p2p->req_dev_types); + p2p->req_dev_types = NULL; +} + + +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id) +{ + int res; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)", + type); + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is " + "already running"); + } + + p2p_free_req_dev_types(p2p); + if (req_dev_types && num_req_dev_types) { + p2p->req_dev_types = os_malloc(num_req_dev_types * + WPS_DEV_TYPE_LEN); + if (p2p->req_dev_types == NULL) + return -1; + os_memcpy(p2p->req_dev_types, req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); + p2p->num_req_dev_types = num_req_dev_types; + } + + if (dev_id) { + os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN); + p2p->find_dev_id = p2p->find_dev_id_buf; + } else + p2p->find_dev_id = NULL; + + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + p2p_clear_timeout(p2p); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_type = type; + p2p_device_clear_reported(p2p); + p2p_set_state(p2p, P2P_SEARCH); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p->last_p2p_find_timeout = timeout; + if (timeout) + eloop_register_timeout(timeout, 0, p2p_find_timeout, + p2p, NULL); + switch (type) { + case P2P_FIND_START_WITH_FULL: + case P2P_FIND_PROGRESSIVE: + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, + p2p->num_req_dev_types, + p2p->req_dev_types, dev_id); + break; + case P2P_FIND_ONLY_SOCIAL: + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, + p2p->num_req_dev_types, + p2p->req_dev_types, dev_id); + break; + default: + return -1; + } + + if (res == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } else if (res == 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " + "p2p_scan at this point - will try again after " + "previous scan completes"); + res = 0; + p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " + "p2p_scan"); + p2p_set_state(p2p, P2P_IDLE); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } + + return res; +} + + +int p2p_other_scan_completed(struct p2p_data *p2p) +{ + if (p2p->state != P2P_SEARCH_WHEN_READY) + return 0; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " + "now that previous scan was completed"); + if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id) < 0) + return 0; + return 1; +} + + +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find"); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p_clear_timeout(p2p); + if (p2p->state == P2P_SEARCH) + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED); + p2p_set_state(p2p, P2P_IDLE); + p2p_free_req_dev_types(p2p); + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + p2p->go_neg_peer = NULL; + p2p->sd_peer = NULL; + p2p->invite_peer = NULL; + p2p_stop_listen_for_freq(p2p, freq); +} + + +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) +{ + if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen " + "since we are on correct channel for response"); + return; + } + if (p2p->in_listen) { + p2p->in_listen = 0; + p2p_clear_timeout(p2p); + } + if (p2p->drv_in_listen) { + /* + * The driver may not deliver callback to p2p_listen_end() + * when the operation gets canceled, so clear the internal + * variable that is tracking driver state. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear " + "drv_in_listen (%d)", p2p->drv_in_listen); + p2p->drv_in_listen = 0; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); +} + + +void p2p_stop_find(struct p2p_data *p2p) +{ + p2p_stop_find_for_freq(p2p, 0); +} + + +static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq) +{ + if (force_freq) { + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(p2p->cfg->country, force_freq, + &op_reg_class, &op_channel) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported frequency %u MHz", + force_freq); + return -1; + } + if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Frequency %u MHz (oper_class %u " + "channel %u) not allowed for P2P", + force_freq, op_reg_class, op_channel); + return -1; + } + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + p2p->channels.reg_classes = 1; + p2p->channels.reg_class[0].channels = 1; + p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; + p2p->channels.reg_class[0].channel[0] = p2p->op_channel; + } else { + u8 op_reg_class, op_channel; + + if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && + p2p_supported_freq(p2p, p2p->best_freq_overall) && + p2p_freq_to_channel(p2p->cfg->country, + p2p->best_freq_overall, + &op_reg_class, &op_channel) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Select best overall channel as " + "operating channel preference"); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_5) && + p2p_freq_to_channel(p2p->cfg->country, + p2p->best_freq_5, + &op_reg_class, &op_channel) == + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Select best 5 GHz channel as " + "operating channel preference"); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && + p2p->best_freq_24 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_24) && + p2p_freq_to_channel(p2p->cfg->country, + p2p->best_freq_24, + &op_reg_class, &op_channel) == + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Select best 2.4 GHz channel as " + "operating channel preference"); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + } else { + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + } + + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Own preference for operation channel: " + "Operating Class %u Channel %u%s", + p2p->op_reg_class, p2p->op_channel, + force_freq ? " (forced)" : ""); + + return 0; +} + + +static void p2p_set_dev_persistent(struct p2p_device *dev, + int persistent_group) +{ + switch (persistent_group) { + case 0: + dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN); + break; + case 1: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP; + dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + case 2: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + } +} + + +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Request to start group negotiation - peer=" MACSTR + " GO Intent=%d Intended Interface Address=" MACSTR + " wps_method=%d persistent_group=%d", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group); + + if (p2p_prepare_channel(p2p, force_freq) < 0) + return -1; + + p2p->ssid_set = 0; + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer_addr)); + return -1; + } + if (dev->oper_freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot connect to P2P Device " MACSTR + " with incomplete information", + MAC2STR(peer_addr)); + return -1; + } + + /* + * First, try to connect directly. If the peer does not + * acknowledge frames, assume it is sleeping and use device + * discoverability via the GO at that point. + */ + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + dev->flags &= ~P2P_DEV_USER_REJECTED; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + dev->connect_reqs = 0; + dev->go_neg_req_sent = 0; + dev->go_state = UNKNOWN_GO; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + if (p2p->after_scan_tx) { + /* + * We need to drop the pending frame to avoid issues with the + * new GO Negotiation, e.g., when the pending frame was from a + * previous attempt at starting a GO Negotiation. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " + "previous pending Action frame TX that was waiting " + "for p2p_scan completion"); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + dev->wps_method = wps_method; + dev->status = P2P_SC_SUCCESS; + + if (force_freq) + dev->flags |= P2P_DEV_FORCE_FREQ; + else + dev->flags &= ~P2P_DEV_FORCE_FREQ; + + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: p2p_scan running - delay connect send"); + p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; + os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); + return 0; + } + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + + return p2p_connect_send(p2p, dev); +} + + +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Request to authorize group negotiation - peer=" MACSTR + " GO Intent=%d Intended Interface Address=" MACSTR + " wps_method=%d persistent_group=%d", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group); + + if (p2p_prepare_channel(p2p, force_freq) < 0) + return -1; + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot authorize unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + dev->flags &= ~P2P_DEV_USER_REJECTED; + dev->go_neg_req_sent = 0; + dev->go_state = UNKNOWN_GO; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + dev->wps_method = wps_method; + dev->status = P2P_SC_SUCCESS; + + if (force_freq) + dev->flags |= P2P_DEV_FORCE_FREQ; + else + dev->flags &= ~P2P_DEV_FORCE_FREQ; + + return 0; +} + + +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg) +{ + os_get_time(&dev->last_seen); + + p2p_copy_wps_info(dev, 0, msg); + + if (msg->listen_channel) { + int freq; + freq = p2p_channel_to_freq((char *) msg->listen_channel, + msg->listen_channel[3], + msg->listen_channel[4]); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown peer Listen channel: " + "country=%c%c(0x%02x) reg_class=%u channel=%u", + msg->listen_channel[0], + msg->listen_channel[1], + msg->listen_channel[2], + msg->listen_channel[3], + msg->listen_channel[4]); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update " + "peer " MACSTR " Listen channel: %u -> %u MHz", + MAC2STR(dev->info.p2p_device_addr), + dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Completed device entry based on data from " + "GO Negotiation Request"); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Created device entry based on GO Neg Req: " + MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " + "listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), + dev->info.dev_capab, dev->info.group_capab, + dev->info.device_name, dev->listen_freq); + } + + dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; + + if (dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not report rejected device"); + return; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; +} + + +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) +{ + os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2); + os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2], + p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len); + *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len; +} + + +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) +{ + p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + p2p_random(params->passphrase, 8); + return 0; +} + + +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) +{ + struct p2p_go_neg_results res; + int go = peer->go_state == LOCAL_GO; + struct p2p_channels intersection; + int freqs; + size_t i, j; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR " completed (%s will be " + "GO)", MAC2STR(peer->info.p2p_device_addr), + go ? "local end" : "peer"); + + os_memset(&res, 0, sizeof(res)); + res.role_go = go; + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); + res.wps_method = peer->wps_method; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + res.persistent_group = 2; + else + res.persistent_group = 1; + } + + if (go) { + /* Setup AP mode for WPS provisioning */ + res.freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + p2p_random(res.passphrase, 8); + } else { + res.freq = peer->oper_freq; + if (p2p->ssid_len) { + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + } + } + + p2p_channels_intersect(&p2p->channels, &peer->channels, + &intersection); + freqs = 0; + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c = &intersection.reg_class[i]; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + for (j = 0; j < c->channels; j++) { + int freq; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + freq = p2p_channel_to_freq(peer->country, c->reg_class, + c->channel[j]); + if (freq < 0) + continue; + res.freq_list[freqs++] = freq; + } + } + + res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; + + p2p_clear_timeout(p2p); + p2p->ssid_set = 0; + peer->go_neg_req_sent = 0; + peer->wps_method = WPS_NOT_READY; + + p2p_set_state(p2p, P2P_PROVISIONING); + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); + + if (len < 1) + return; + + switch (data[0]) { + case P2P_GO_NEG_REQ: + p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_RESP: + p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_CONF: + p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); + break; + case P2P_INVITATION_REQ: + p2p_process_invitation_req(p2p, sa, data + 1, len - 1, + rx_freq); + break; + case P2P_INVITATION_RESP: + p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_PROV_DISC_REQ: + p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_PROV_DISC_RESP: + p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_DEV_DISC_REQ: + p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_DEV_DISC_RESP: + p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported P2P Public Action frame type %d", + data[0]); + break; + } +} + + +void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, const u8 *data, size_t len, + int freq) +{ + if (len < 1) + return; + + switch (data[0]) { + case WLAN_PA_VENDOR_SPECIFIC: + data++; + len--; + if (len < 3) + return; + if (WPA_GET_BE24(data) != OUI_WFA) + return; + + data += 3; + len -= 3; + if (len < 1) + return; + + if (*data != P2P_OUI_TYPE) + return; + + p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_REQ: + p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_RESP: + p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_RESP: + p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq); + break; + } +} + + +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq) +{ + if (category == WLAN_ACTION_PUBLIC) { + p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq); + return; + } + + if (category != WLAN_ACTION_VENDOR_SPECIFIC) + return; + + if (len < 4) + return; + + if (WPA_GET_BE24(data) != OUI_WFA) + return; + data += 3; + len -= 3; + + if (*data != P2P_OUI_TYPE) + return; + data++; + len--; + + /* P2P action frame */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: RX P2P Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); + + if (len < 1) + return; + switch (data[0]) { + case P2P_NOA: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - Notice of Absence"); + /* TODO */ + break; + case P2P_PRESENCE_REQ: + p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq); + break; + case P2P_PRESENCE_RESP: + p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1); + break; + case P2P_GO_DISC_REQ: + p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - unknown type %u", data[0]); + break; + } +} + + +static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->go_neg_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->go_neg_peer->status = P2P_SC_SUCCESS; + p2p_connect_send(p2p, p2p->go_neg_peer); +} + + +static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->invite_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr); +} + + +static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + struct p2p_device *dev; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL) + { + p2p_parse_free(&msg); + return; /* not a P2P probe */ + } + + if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0) { + /* The Probe Request is not part of P2P Device Discovery. It is + * not known whether the source address of the frame is the P2P + * Device Address or P2P Interface Address. Do not add a new + * peer entry based on this frames. + */ + p2p_parse_free(&msg); + return; + } + + dev = p2p_get_device(p2p, addr); + if (dev) { + if (dev->country[0] == 0 && msg.listen_channel) + os_memcpy(dev->country, msg.listen_channel, 3); + os_get_time(&dev->last_seen); + p2p_parse_free(&msg); + return; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return; + } + + os_get_time(&dev->last_seen); + dev->flags |= P2P_DEV_PROBE_REQ_ONLY; + + if (msg.listen_channel) { + os_memcpy(dev->country, msg.listen_channel, 3); + dev->listen_freq = p2p_channel_to_freq(dev->country, + msg.listen_channel[3], + msg.listen_channel[4]); + } + + p2p_copy_wps_info(dev, 1, &msg); + + p2p_parse_free(&msg); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Created device entry based on Probe Req: " MACSTR + " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, + dev->info.group_capab, dev->info.device_name, + dev->listen_freq); +} + + +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev) { + os_get_time(&dev->last_seen); + return dev; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) + return NULL; + + p2p_add_dev_info(p2p, addr, dev, msg); + + return dev; +} + + +static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type) +{ + if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0) + return 1; + if (os_memcmp(dev_type, req_dev_type, 2) == 0 && + WPA_GET_BE32(&req_dev_type[2]) == 0 && + WPA_GET_BE16(&req_dev_type[6]) == 0) + return 1; /* Category match with wildcard OUI/sub-category */ + return 0; +} + + +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type) +{ + size_t i; + for (i = 0; i < num_req_dev_type; i++) { + if (dev_type_match(dev_type, req_dev_type[i])) + return 1; + } + return 0; +} + + +/** + * p2p_match_dev_type - Match local device type with requested type + * @p2p: P2P module context from p2p_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the local device types for deciding whether to reply to a Probe + * Request frame. + */ +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) +{ + struct wps_parse_attr attr; + size_t i; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Primary Device Type matches */ + + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + if (dev_type_list_match(p2p->cfg->sec_dev_type[i], + attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Secondary Device Type matches */ + + /* No matching device type found */ + return 0; +} + + +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1); + + /* P2P IE */ + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_capability(buf, p2p->dev_capab, 0); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_device_info(buf, p2p, NULL); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +static int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} + + +static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, + const u8 *dst, const u8 *bssid, const u8 *ie, + size_t ie_len) +{ + struct ieee802_11_elems elems; + struct wpabuf *buf; + struct ieee80211_mgmt *resp; + struct p2p_message msg; + struct wpabuf *ies; + + if (!p2p->in_listen || !p2p->drv_in_listen) { + /* not in Listen state - ignore Probe Request */ + return; + } + + if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == + ParseFailed) { + /* Ignore invalid Probe Request frames */ + return; + } + + if (elems.p2p == NULL) { + /* not a P2P probe - ignore it */ + return; + } + + if (dst && !is_broadcast_ether_addr(dst) && + os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + /* Not sent to the broadcast address or our P2P Device Address + */ + return; + } + + if (bssid && !is_broadcast_ether_addr(bssid)) { + /* Not sent to the Wildcard BSSID */ + return; + } + + if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != + 0) { + /* not using P2P Wildcard SSID - ignore */ + return; + } + + if (supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + return; + } + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg) < 0) { + /* Could not parse P2P attributes */ + return; + } + + if (msg.device_id && + os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) { + /* Device ID did not match */ + p2p_parse_free(&msg); + return; + } + + /* Check Requested Device Type match */ + if (msg.wps_attributes && + !p2p_match_dev_type(p2p, msg.wps_attributes)) { + /* No match with Requested Device Type */ + p2p_parse_free(&msg); + return; + } + p2p_parse_free(&msg); + + if (!p2p->cfg->send_probe_resp) + return; /* Response generated elsewhere */ + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Reply to P2P Probe Request in Listen state"); + + /* + * We do not really have a specific BSS that this frame is advertising, + * so build a frame that has some information in valid format. This is + * really only used for discovery purposes, not to learn exact BSS + * parameters. + */ + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (buf == NULL) { + wpabuf_free(ies); + return; + } + + resp = NULL; + resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, p2p->cfg->channel); + + wpabuf_put_buf(buf, ies); + wpabuf_free(ies); + + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + + wpabuf_free(buf); +} + + +int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) +{ + p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); + + p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + + if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && + p2p->go_neg_peer && + os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) + == 0) { + /* Received a Probe Request from GO Negotiation peer */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Found GO Negotiation peer - try to start GO " + "negotiation from timeout"); + eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); + return 1; + } + + if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && + p2p->invite_peer && + os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) + == 0) { + /* Received a Probe Request from Invite peer */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Found Invite peer - try to start Invite from " + "timeout"); + eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); + return 1; + } + + return 0; +} + + +static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, + u8 *buf, size_t len, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + size_t tmplen; + int res; + u8 group_capab; + + if (p2p_ie == NULL) + return 0; /* WLAN AP is not a P2P manager */ + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * P2P Interface attribute (present if concurrent device and + * P2P Management is enabled) + */ + tmp = wpabuf_alloc(200); + if (tmp == NULL) + return -1; + + lpos = p2p_buf_add_ie_hdr(tmp); + group_capab = 0; + if (p2p->num_groups > 0) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) + p2p_buf_add_p2p_interface(tmp, p2p); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + struct p2p_device *peer; + size_t tmplen; + int res; + + if (!p2p_group) + return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * Extended Listen Timing (may be present) + * P2P Device Info attribute (shall be present) + */ + tmp = wpabuf_alloc(200); + if (tmp == NULL) + return -1; + + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; + + lpos = p2p_buf_add_ie_hdr(tmp); + p2p_buf_add_capability(tmp, p2p->dev_capab, 0); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_device_info(tmp, p2p, peer); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) +{ + struct wpabuf *p2p_ie; + int ret; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return 0; + + ret = p2p_attr_text(p2p_ie, buf, end); + wpabuf_free(p2p_ie); + return ret; +} + + +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +{ + struct wpabuf *p2p_ie; + struct p2p_message msg; + int ret = -1; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return -1; + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) { + wpabuf_free(p2p_ie); + return -1; + } + + if (msg.p2p_device_addr) { + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + ret = 0; + } else if (msg.device_id) { + os_memcpy(dev_addr, msg.device_id, ETH_ALEN); + ret = 0; + } + + wpabuf_free(p2p_ie); + return ret; +} + + +static void p2p_clear_go_neg(struct p2p_data *p2p) +{ + p2p->go_neg_peer = NULL; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); +} + + +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) +{ + if (p2p->go_neg_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Group Formation - " + "ignore WPS registration success notification"); + return; /* No pending Group Formation */ + } + + if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore WPS registration success notification " + "for " MACSTR " (GO Negotiation peer " MACSTR ")", + MAC2STR(mac_addr), + MAC2STR(p2p->go_neg_peer->intended_addr)); + return; /* Ignore unexpected peer address */ + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Group Formation completed successfully with " MACSTR, + MAC2STR(mac_addr)); + + p2p_clear_go_neg(p2p); +} + + +void p2p_group_formation_failed(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Group Formation - " + "ignore group formation failure notification"); + return; /* No pending Group Formation */ + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Group Formation failed with " MACSTR, + MAC2STR(p2p->go_neg_peer->intended_addr)); + + p2p_clear_go_neg(p2p); +} + + +struct p2p_data * p2p_init(const struct p2p_config *cfg) +{ + struct p2p_data *p2p; + + if (cfg->max_peers < 1) + return NULL; + + p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); + if (p2p == NULL) + return NULL; + p2p->cfg = (struct p2p_config *) (p2p + 1); + os_memcpy(p2p->cfg, cfg, sizeof(*cfg)); + if (cfg->dev_name) + p2p->cfg->dev_name = os_strdup(cfg->dev_name); + if (cfg->manufacturer) + p2p->cfg->manufacturer = os_strdup(cfg->manufacturer); + if (cfg->model_name) + p2p->cfg->model_name = os_strdup(cfg->model_name); + if (cfg->model_number) + p2p->cfg->model_number = os_strdup(cfg->model_number); + if (cfg->serial_number) + p2p->cfg->serial_number = os_strdup(cfg->serial_number); + if (cfg->pref_chan) { + p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan * + sizeof(struct p2p_channel)); + if (p2p->cfg->pref_chan) { + os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan, + cfg->num_pref_chan * + sizeof(struct p2p_channel)); + } else + p2p->cfg->num_pref_chan = 0; + } + + p2p->min_disc_int = 1; + p2p->max_disc_int = 3; + + os_get_random(&p2p->next_tie_breaker, 1); + p2p->next_tie_breaker &= 0x01; + if (cfg->sd_request) + p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + if (cfg->concurrent_operations) + p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER; + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + + dl_list_init(&p2p->devices); + + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); + + return p2p; +} + + +void p2p_deinit(struct p2p_data *p2p) +{ + eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + p2p_flush(p2p); + p2p_free_req_dev_types(p2p); + os_free(p2p->cfg->dev_name); + os_free(p2p->cfg->manufacturer); + os_free(p2p->cfg->model_name); + os_free(p2p->cfg->model_number); + os_free(p2p->cfg->serial_number); + os_free(p2p->cfg->pref_chan); + os_free(p2p->groups); + wpabuf_free(p2p->sd_resp); + os_free(p2p->after_scan_tx); + p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p); +} + + +void p2p_flush(struct p2p_data *p2p) +{ + struct p2p_device *dev, *prev; + p2p_stop_find(p2p); + dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, + list) { + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } + p2p_free_sd_queries(p2p); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; +} + + +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR, + MAC2STR(addr)); + + if (p2p->go_neg_peer == dev) + p2p->go_neg_peer = NULL; + + dev->wps_method = WPS_NOT_READY; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + /* Check if after_scan_tx is for this peer. If so free it */ + if (p2p->after_scan_tx && + os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) { + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + return 0; +} + + +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name) +{ + os_free(p2p->cfg->dev_name); + if (dev_name) { + p2p->cfg->dev_name = os_strdup(dev_name); + if (p2p->cfg->dev_name == NULL) + return -1; + } else + p2p->cfg->dev_name = NULL; + return 0; +} + + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer) +{ + os_free(p2p->cfg->manufacturer); + p2p->cfg->manufacturer = NULL; + if (manufacturer) { + p2p->cfg->manufacturer = os_strdup(manufacturer); + if (p2p->cfg->manufacturer == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name) +{ + os_free(p2p->cfg->model_name); + p2p->cfg->model_name = NULL; + if (model_name) { + p2p->cfg->model_name = os_strdup(model_name); + if (p2p->cfg->model_name == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number) +{ + os_free(p2p->cfg->model_number); + p2p->cfg->model_number = NULL; + if (model_number) { + p2p->cfg->model_number = os_strdup(model_number); + if (p2p->cfg->model_number == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number) +{ + os_free(p2p->cfg->serial_number); + p2p->cfg->serial_number = NULL; + if (serial_number) { + p2p->cfg->serial_number = os_strdup(serial_number); + if (p2p->cfg->serial_number == NULL) + return -1; + } + + return 0; +} + + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods) +{ + p2p->cfg->config_methods = config_methods; +} + + +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid) +{ + os_memcpy(p2p->cfg->uuid, uuid, 16); +} + + +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type) +{ + os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8); + return 0; +} + + +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types) +{ + if (num_dev_types > P2P_SEC_DEVICE_TYPES) + num_dev_types = P2P_SEC_DEVICE_TYPES; + p2p->cfg->num_sec_dev_types = num_dev_types; + os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8); + return 0; +} + + +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p) +{ + int i; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(p2p->wps_vendor_ext[i]); + p2p->wps_vendor_ext[i] = NULL; + } +} + + +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext) +{ + int i; + + if (vendor_ext == NULL) + return -1; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + } + if (i >= P2P_MAX_WPS_VENDOR_EXT) + return -1; + + p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext); + if (p2p->wps_vendor_ext[i] == NULL) + return -1; + + return 0; +} + + +int p2p_set_country(struct p2p_data *p2p, const char *country) +{ + os_memcpy(p2p->cfg->country, country, 3); + return 0; +} + + +void p2p_continue_find(struct p2p_data *p2p) +{ + struct p2p_device *dev; + p2p_set_state(p2p, P2P_SEARCH); + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (dev->flags & P2P_DEV_SD_SCHEDULE) { + if (p2p_start_sd(p2p, dev) == 0) + return; + else + break; + } else if (dev->req_config_methods && + !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " + "pending Provisioning Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) + return; + } + } + + p2p_listen_in_find(p2p); +} + + +static void p2p_sd_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery Query TX callback: success=%d", + success); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (!success) { + if (p2p->sd_peer) { + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); + return; + } + + if (p2p->sd_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No SD peer entry known"); + p2p_continue_find(p2p); + return; + } + + /* Wait for response from the peer */ + p2p_set_state(p2p, P2P_SD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +/** + * p2p_retry_pd - Retry any pending provision disc requests in IDLE state + * @p2p: P2P module context from p2p_init() + */ +void p2p_retry_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + if (p2p->state != P2P_IDLE) + return; + + /* + * Retry the prov disc req attempt only for the peer that the user had + * requested for and provided a join has not been initiated on it + * in the meantime. + */ + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (!dev->req_config_methods) + continue; + if (dev->flags & P2P_DEV_PD_FOR_JOIN) + continue; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " + "pending Provisioning Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + p2p_send_prov_disc_req(p2p, dev, 0, 0); + return; + } +} + + +static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request TX callback: success=%d", + success); + + /* + * Postpone resetting the pending action state till after we actually + * time out. This allows us to take some action like notifying any + * interested parties about no response to the request. + * + * When the timer (below) goes off we check in IDLE, SEARCH, or + * LISTEN_ONLY state, which are the only allowed states to issue a PD + * requests in, if this was still pending and then raise notification. + */ + + if (!success) { + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); + else if (p2p->user_initiated_pd) { + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 300000); + } + return; + } + + /* + * This postponing, of resetting pending_action_state, needs to be + * done only for user initiated PD requests and not internal ones. + */ + if (p2p->user_initiated_pd) + p2p->pending_action_state = P2P_PENDING_PD; + else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* Wait for response from the peer */ + if (p2p->state == P2P_SEARCH) + p2p_set_state(p2p, P2P_PD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + int level, const u8 *ies, size_t ies_len) +{ + p2p_add_device(p2p, bssid, freq, level, ies, ies_len); + + if (p2p->go_neg_peer && p2p->state == P2P_SEARCH && + os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN) + == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Found GO Negotiation peer - try to start GO " + "negotiation"); + p2p_connect_send(p2p, p2p->go_neg_peer); + return 1; + } + + return 0; +} + + +void p2p_scan_res_handled(struct p2p_data *p2p) +{ + if (!p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not " + "running, but scan results received"); + } + p2p->p2p_scan_running = 0; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + + if (p2p_run_after_scan(p2p)) + return; + if (p2p->state == P2P_SEARCH) + p2p_continue_find(p2p); +} + + +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +{ + u8 *len = p2p_buf_add_ie_hdr(ies); + p2p_buf_add_capability(ies, p2p->dev_capab, 0); + if (dev_id) + p2p_buf_add_device_id(ies, dev_id); + if (p2p->cfg->reg_class && p2p->cfg->channel) + p2p_buf_add_listen_channel(ies, p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, + p2p->ext_listen_interval); + /* TODO: p2p_buf_add_operating_channel() if GO */ + p2p_buf_update_ie_hdr(ies, len); +} + + +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) +{ + return 100; +} + + +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) +{ + return p2p_attr_text(p2p_ie, buf, end); +} + + +static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) +{ + struct p2p_device *dev = p2p->go_neg_peer; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Request TX callback: success=%d", + success); + + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending GO Negotiation"); + return; + } + + if (success) { + dev->go_neg_req_sent++; + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_set_state(p2p, P2P_IDLE); + return; + } + } + + if (!success && + (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && + !is_zero_ether_addr(dev->member_in_go_dev)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer " MACSTR " did not acknowledge request - " + "try to use device discoverability through its GO", + MAC2STR(dev->info.p2p_device_addr)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_send_dev_disc_req(p2p, dev); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 100000); +} + + +static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Response TX callback: success=%d", + success); + if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore TX callback event - GO Negotiation is " + "not running anymore"); + return; + } + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 100000); +} + + +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Response (failure) TX callback: " + "success=%d", success); + if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, + p2p->go_neg_peer->status); + } +} + + +static void p2p_go_neg_conf_cb(struct p2p_data *p2p, + enum p2p_send_action_result result) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation Confirm TX callback: result=%d", + result); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (result == P2P_SEND_ACTION_FAILED) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + if (result == P2P_SEND_ACTION_NO_ACK) { + /* + * It looks like the TX status for GO Negotiation Confirm is + * often showing failure even when the peer has actually + * received the frame. Since the peer may change channels + * immediately after having received the frame, we may not see + * an Ack for retries, so just dropping a single frame may + * trigger this. To allow the group formation to succeed if the + * peer did indeed receive the frame, continue regardless of + * the TX status. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Assume GO Negotiation Confirm TX was actually " + "received by the peer even though Ack was not " + "reported"); + } + + dev = p2p->go_neg_peer; + if (dev == NULL) + return; + + p2p_go_complete(p2p, dev); +} + + +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result) +{ + enum p2p_pending_action_state state; + int success; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%d", + p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), + MAC2STR(bssid), result); + success = result == P2P_SEND_ACTION_SUCCESS; + state = p2p->pending_action_state; + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + switch (state) { + case P2P_NO_PENDING_ACTION: + break; + case P2P_PENDING_GO_NEG_REQUEST: + p2p_go_neg_req_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE: + p2p_go_neg_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: + p2p_go_neg_resp_failure_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_CONFIRM: + p2p_go_neg_conf_cb(p2p, result); + break; + case P2P_PENDING_SD: + p2p_sd_cb(p2p, success); + break; + case P2P_PENDING_PD: + p2p_prov_disc_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_REQUEST: + p2p_invitation_req_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_RESPONSE: + p2p_invitation_resp_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_REQUEST: + p2p_dev_disc_req_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_RESPONSE: + p2p_dev_disc_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_DISC_REQ: + p2p_go_disc_req_cb(p2p, success); + break; + } +} + + +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration) +{ + if (freq == p2p->pending_client_disc_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability remain-awake completed"); + p2p->pending_client_disc_freq = 0; + return; + } + + if (freq != p2p->pending_listen_freq) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected listen callback for freq=%u " + "duration=%u (pending_listen_freq=%u)", + freq, duration, p2p->pending_listen_freq); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Starting Listen timeout(%u,%u) on freq=%u based on " + "callback", + p2p->pending_listen_sec, p2p->pending_listen_usec, + p2p->pending_listen_freq); + p2p->in_listen = 1; + p2p->drv_in_listen = freq; + if (p2p->pending_listen_sec || p2p->pending_listen_usec) { + /* + * Add 20 msec extra wait to avoid race condition with driver + * remain-on-channel end event, i.e., give driver more time to + * complete the operation before our timeout expires. + */ + p2p_set_timeout(p2p, p2p->pending_listen_sec, + p2p->pending_listen_usec + 20000); + } + + p2p->pending_listen_freq = 0; +} + + +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen " + "state (freq=%u)", freq); + p2p->drv_in_listen = 0; + if (p2p->in_listen) + return 0; /* Internal timeout will trigger the next step */ + + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { + if (p2p->go_neg_peer->connect_reqs >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on sending GO Negotiation " + "Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return 0; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + return 1; + } else if (p2p->state == P2P_SEARCH) { + if (p2p->p2p_scan_running) { + /* + * Search is already in progress. This can happen if + * an Action frame RX is reported immediately after + * the end of a remain-on-channel operation and the + * response frame to that is sent using an offchannel + * operation while in p2p_find. Avoid an attempt to + * restart a scan here. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan " + "already in progress - do not try to start a " + "new one"); + return 1; + } + p2p_search(p2p); + return 1; + } + + return 0; +} + + +static void p2p_timeout_connect(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_listen_in_find(p2p); +} + + +static void p2p_timeout_connect_listen(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer) { + if (p2p->drv_in_listen) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is " + "still in Listen state; wait for it to " + "complete"); + return; + } + + if (p2p->go_neg_peer->connect_reqs >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on sending GO Negotiation " + "Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + } else + p2p_set_state(p2p, P2P_IDLE); +} + + +static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) +{ + /* + * TODO: could remain constantly in Listen state for some time if there + * are no other concurrent uses for the radio. For now, go to listen + * state once per second to give other uses a chance to use the radio. + */ + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 500000); +} + + +static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) +{ + struct p2p_device *dev = p2p->go_neg_peer; + + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown GO Neg peer - stop GO Neg wait"); + return; + } + + dev->wait_count++; + if (dev->wait_count >= 120) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Timeout on waiting peer to become ready for GO " + "Negotiation"); + p2p_go_neg_failed(p2p, dev, -1); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Go to Listen state while waiting for the peer to become " + "ready for GO Negotiation"); + p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); + p2p_listen_in_find(p2p); +} + + +static void p2p_timeout_sd_during_find(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery Query timeout"); + if (p2p->sd_peer) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request timeout"); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) +{ + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* + * For user initiated PD requests that we have not gotten any responses + * for while in IDLE state, we retry them a couple of times before + * giving up. + */ + if (!p2p->user_initiated_pd) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: User initiated Provision Discovery Request timeout"); + + if (p2p->pd_retries) { + p2p->pd_retries--; + p2p_retry_pd(p2p); + } else { + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, + p2p->pending_pd_devaddr, + P2P_PROV_DISC_TIMEOUT); + p2p_reset_pending_pd(p2p); + } +} + + +static void p2p_timeout_invite(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_INVITE_LISTEN); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + /* + * Better remain on operating channel instead of listen channel + * when running a group. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in " + "active GO role - wait on operating channel"); + p2p_set_timeout(p2p, 0, 100000); + return; + } + p2p_listen_in_find(p2p); +} + + +static void p2p_timeout_invite_listen(struct p2p_data *p2p) +{ + if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { + p2p_set_state(p2p, P2P_INVITE); + p2p_invite_send(p2p, p2p->invite_peer, + p2p->invite_go_dev_addr); + } else { + if (p2p->invite_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request retry limit reached"); + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result( + p2p->cfg->cb_ctx, -1, NULL); + } + p2p_set_state(p2p, P2P_IDLE); + } +} + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)", + p2p_state_txt(p2p->state)); + + p2p->in_listen = 0; + + switch (p2p->state) { + case P2P_IDLE: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + break; + case P2P_SEARCH: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + p2p_search(p2p); + break; + case P2P_CONNECT: + p2p_timeout_connect(p2p); + break; + case P2P_CONNECT_LISTEN: + p2p_timeout_connect_listen(p2p); + break; + case P2P_GO_NEG: + break; + case P2P_LISTEN_ONLY: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + + if (p2p->ext_listen_only) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Extended Listen Timing - Listen State " + "completed"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + break; + case P2P_WAIT_PEER_CONNECT: + p2p_timeout_wait_peer_connect(p2p); + break; + case P2P_WAIT_PEER_IDLE: + p2p_timeout_wait_peer_idle(p2p); + break; + case P2P_SD_DURING_FIND: + p2p_timeout_sd_during_find(p2p); + break; + case P2P_PROVISIONING: + break; + case P2P_PD_DURING_FIND: + p2p_timeout_prov_disc_during_find(p2p); + break; + case P2P_INVITE: + p2p_timeout_invite(p2p); + break; + case P2P_INVITE_LISTEN: + p2p_timeout_invite_listen(p2p); + break; + case P2P_SEARCH_WHEN_READY: + break; + } +} + + +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject " + "connection attempts by peer " MACSTR, MAC2STR(peer_addr)); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " unknown", MAC2STR(peer_addr)); + return -1; + } + dev->status = P2P_SC_FAIL_REJECTED_BY_USER; + dev->flags |= P2P_DEV_USER_REJECTED; + return 0; +} + + +const char * p2p_wps_method_text(enum p2p_wps_method method) +{ + switch (method) { + case WPS_NOT_READY: + return "not-ready"; + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + } + + return "??"; +} + + +static const char * p2p_go_state_text(enum p2p_go_state go_state) +{ + switch (go_state) { + case UNKNOWN_GO: + return "unknown"; + case LOCAL_GO: + return "local"; + case REMOTE_GO: + return "remote"; + } + + return "??"; +} + + +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) + dev = p2p_get_device(p2p, addr); + else + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + + if (dev && next) { + dev = dl_list_first(&dev->list, struct p2p_device, list); + if (&dev->list == &p2p->devices) + dev = NULL; + } + + if (dev == NULL) + return NULL; + + return &dev->info; +} + + +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen) +{ + struct p2p_device *dev; + int res; + char *pos, *end; + struct os_time now; + + if (info == NULL) + return -1; + + dev = (struct p2p_device *) (((u8 *) info) - + offsetof(struct p2p_device, info)); + + pos = buf; + end = buf + buflen; + + os_get_time(&now); + res = os_snprintf(pos, end - pos, + "age=%d\n" + "listen_freq=%d\n" + "wps_method=%s\n" + "interface_addr=" MACSTR "\n" + "member_in_go_dev=" MACSTR "\n" + "member_in_go_iface=" MACSTR "\n" + "go_neg_req_sent=%d\n" + "go_state=%s\n" + "dialog_token=%u\n" + "intended_addr=" MACSTR "\n" + "country=%c%c\n" + "oper_freq=%d\n" + "req_config_methods=0x%x\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "status=%d\n" + "wait_count=%u\n" + "invitation_reqs=%u\n", + (int) (now.sec - dev->last_seen.sec), + dev->listen_freq, + p2p_wps_method_text(dev->wps_method), + MAC2STR(dev->interface_addr), + MAC2STR(dev->member_in_go_dev), + MAC2STR(dev->member_in_go_iface), + dev->go_neg_req_sent, + p2p_go_state_text(dev->go_state), + dev->dialog_token, + MAC2STR(dev->intended_addr), + dev->country[0] ? dev->country[0] : '_', + dev->country[1] ? dev->country[1] : '_', + dev->oper_freq, + dev->req_config_methods, + dev->flags & P2P_DEV_PROBE_REQ_ONLY ? + "[PROBE_REQ_ONLY]" : "", + dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", + dev->flags & P2P_DEV_NOT_YET_READY ? + "[NOT_YET_READY]" : "", + dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", + dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : + "", + dev->flags & P2P_DEV_PD_PEER_DISPLAY ? + "[PD_PEER_DISPLAY]" : "", + dev->flags & P2P_DEV_PD_PEER_KEYPAD ? + "[PD_PEER_KEYPAD]" : "", + dev->flags & P2P_DEV_USER_REJECTED ? + "[USER_REJECTED]" : "", + dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? + "[PEER_WAITING_RESPONSE]" : "", + dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ? + "[PREFER_PERSISTENT_GROUP]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ? + "[WAIT_GO_NEG_RESPONSE]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ? + "[WAIT_GO_NEG_CONFIRM]" : "", + dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ? + "[GROUP_CLIENT_ONLY]" : "", + dev->flags & P2P_DEV_FORCE_FREQ ? + "[FORCE_FREQ]" : "", + dev->flags & P2P_DEV_PD_FOR_JOIN ? + "[PD_FOR_JOIN]" : "", + dev->status, + dev->wait_count, + dev->invitation_reqs); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (dev->ext_listen_period) { + res = os_snprintf(pos, end - pos, + "ext_listen_period=%u\n" + "ext_listen_interval=%u\n", + dev->ext_listen_period, + dev->ext_listen_interval); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + if (dev->oper_ssid_len) { + res = os_snprintf(pos, end - pos, + "oper_ssid=%s\n", + wpa_ssid_txt(dev->oper_ssid, + dev->oper_ssid_len)); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + return pos - buf; +} + + +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) +{ + return p2p_get_device(p2p, addr) != NULL; +} + + +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " + "discoverability enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " + "discoverability disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } +} + + +static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1, + u32 duration2, u32 interval2) +{ + struct wpabuf *req; + struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL; + u8 *len; + + req = wpabuf_alloc(100); + if (req == NULL) + return NULL; + + if (duration1 || interval1) { + os_memset(&desc1, 0, sizeof(desc1)); + desc1.count_type = 1; + desc1.duration = duration1; + desc1.interval = interval1; + ptr1 = &desc1; + + if (duration2 || interval2) { + os_memset(&desc2, 0, sizeof(desc2)); + desc2.count_type = 2; + desc2.duration = duration2; + desc2.interval = interval2; + ptr2 = &desc2; + } + } + + p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1); + len = p2p_buf_add_ie_hdr(req); + p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2); + p2p_buf_update_ie_hdr(req, len); + + return req; +} + + +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2) +{ + struct wpabuf *req; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to " + "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u " + "int1=%u dur2=%u int2=%u", + MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), + freq, duration1, interval1, duration2, interval2); + + req = p2p_build_presence_req(duration1, interval1, duration2, + interval2); + if (req == NULL) + return -1; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, + go_interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa, + size_t noa_len, u8 dialog_token) +{ + struct wpabuf *resp; + u8 *len; + + resp = wpabuf_alloc(100 + noa_len); + if (resp == NULL) + return NULL; + + p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token); + len = p2p_buf_add_ie_hdr(resp); + p2p_buf_add_status(resp, status); + if (noa) { + wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(resp, noa_len); + wpabuf_put_data(resp, noa, noa_len); + } else + p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL); + p2p_buf_update_ie_hdr(resp, len); + + return resp; +} + + +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq) +{ + struct p2p_message msg; + u8 status; + struct wpabuf *resp; + size_t g; + struct p2p_group *group = NULL; + int parsed = 0; + u8 noa[50]; + int noa_len; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - P2P Presence Request"); + + for (g = 0; g < p2p->num_groups; g++) { + if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), + ETH_ALEN) == 0) { + group = p2p->groups[g]; + break; + } + } + if (group == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore P2P Presence Request for unknown group " + MACSTR, MAC2STR(da)); + return; + } + + if (p2p_parse(data, len, &msg) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + parsed = 1; + + if (msg.noa == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No NoA attribute in P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len); + +fail: + if (p2p->cfg->get_noa) + noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa, + sizeof(noa)); + else + noa_len = -1; + resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL, + noa_len > 0 ? noa_len : 0, + msg.dialog_token); + if (parsed) + p2p_parse_free(&msg); + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, da, da, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + wpabuf_free(resp); +} + + +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received P2P Action - P2P Presence Response"); + + if (p2p_parse(data, len, &msg) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to parse P2P Presence Response"); + return; + } + + if (msg.status == NULL || msg.noa == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status or NoA attribute in P2P Presence " + "Response"); + p2p_parse_free(&msg); + return; + } + + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Presence Request was rejected: status %u", + *msg.status); + p2p_parse_free(&msg); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Presence Request was accepted"); + wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", + msg.noa, msg.noa_len); + /* TODO: process NoA */ + p2p_parse_free(&msg); +} + + +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + if (p2p->ext_listen_interval) { + /* Schedule next extended listen timeout */ + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + } + + if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { + /* + * This should not really happen, but it looks like the Listen + * command may fail is something else (e.g., a scan) was + * running at an inconvenient time. As a workaround, allow new + * Extended Listen operation to be started. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous " + "Extended Listen operation had not been completed - " + "try again"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + + if (p2p->state != P2P_IDLE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended " + "Listen timeout in active state (%s)", + p2p_state_txt(p2p->state)); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout"); + p2p->ext_listen_only = 1; + if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " + "Listen state for Extended Listen Timing"); + p2p->ext_listen_only = 0; + } +} + + +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval) +{ + if (period > 65535 || interval > 65535 || period > interval || + (period == 0 && interval > 0) || (period > 0 && interval == 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid Extended Listen Timing request: " + "period=%u interval=%u", period, interval); + return -1; + } + + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + + if (interval == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Disabling Extended Listen Timing"); + p2p->ext_listen_period = 0; + p2p->ext_listen_interval = 0; + return 0; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Enabling Extended Listen Timing: period %u msec, " + "interval %u msec", period, interval); + p2p->ext_listen_period = period; + p2p->ext_listen_interval = interval; + p2p->ext_listen_interval_sec = interval / 1000; + p2p->ext_listen_interval_usec = (interval % 1000) * 1000; + + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + + return 0; +} + + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Deauthentication notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Disassociation notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " + "Device operations enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " + "Device operations disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; + } +} + + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +{ + if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " + "reg_class %u channel %u", reg_class, channel); + p2p->cfg->reg_class = reg_class; + p2p->cfg->channel = channel; + + return 0; +} + + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) +{ + wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len); + if (postfix == NULL) { + p2p->cfg->ssid_postfix_len = 0; + return 0; + } + if (len > sizeof(p2p->cfg->ssid_postfix)) + return -1; + os_memcpy(p2p->cfg->ssid_postfix, postfix, len); + p2p->cfg->ssid_postfix_len = len; + return 0; +} + + +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel) +{ + if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel) + < 0) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " + "reg_class %u channel %u", op_reg_class, op_channel); + p2p->cfg->op_reg_class = op_reg_class; + p2p->cfg->op_channel = op_channel; + p2p->cfg->cfg_op_channel = cfg_op_channel; + return 0; +} + + +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan) +{ + struct p2p_channel *n; + + if (pref_chan) { + n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + if (n == NULL) + return -1; + os_memcpy(n, pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); + } else + n = NULL; + + os_free(p2p->cfg->pref_chan); + p2p->cfg->pref_chan = n; + p2p->cfg->num_pref_chan = num_pref_chan; + + return 0; +} + + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr) +{ + struct p2p_device *dev = p2p_get_device(p2p, dev_addr); + if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) + return -1; + os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); + return 0; +} + + +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); + return 0; +} + + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) +{ + os_memcpy(p2p->peer_filter, addr, ETH_ALEN); + if (is_zero_ether_addr(p2p->peer_filter)) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer " + "filter"); + else + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " + "filter for " MACSTR, MAC2STR(p2p->peer_filter)); +} + + +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", + enabled ? "enabled" : "disabled"); + if (p2p->cross_connect == enabled) + return; + p2p->cross_connect = enabled; + /* TODO: may need to tear down any action group where we are GO(?) */ +} + + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + if (dev->oper_freq <= 0) + return -1; + return dev->oper_freq; +} + + +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s", + enabled ? "enabled" : "disabled"); + p2p->cfg->p2p_intra_bss = enabled; +} + + +void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list"); + os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); +} + + +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + if (p2p->p2p_scan_running) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action " + "frame TX until p2p_scan completes"); + if (p2p->after_scan_tx) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " + "previous pending Action frame TX"); + os_free(p2p->after_scan_tx); + } + p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + + len); + if (p2p->after_scan_tx == NULL) + return -1; + p2p->after_scan_tx->freq = freq; + os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN); + p2p->after_scan_tx->len = len; + p2p->after_scan_tx->wait_time = wait_time; + os_memcpy(p2p->after_scan_tx + 1, buf, len); + return 0; + } + + return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time); +} + + +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d," + " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); + p2p->best_freq_24 = freq_24; + p2p->best_freq_5 = freq_5; + p2p->best_freq_overall = freq_overall; +} + + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) +{ + if (p2p == NULL || p2p->go_neg_peer == NULL) + return NULL; + return p2p->go_neg_peer->info.p2p_device_addr; +} + + +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) { + dev = p2p_get_device(p2p, addr); + if (!dev) + return NULL; + + if (!next) { + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + return NULL; + + return &dev->info; + } else { + do { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); + } + } else { + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + if (!dev) + return NULL; + while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } + } + + return &dev->info; +} + + +int p2p_in_progress(struct p2p_data *p2p) +{ + if (p2p == NULL) + return 0; + return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h new file mode 100644 index 0000000..75d4739 --- /dev/null +++ b/src/p2p/p2p.h @@ -0,0 +1,1657 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, 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. + */ + +#ifndef P2P_H +#define P2P_H + +/** + * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes + */ +#define P2P_MAX_REG_CLASSES 10 + +/** + * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class + */ +#define P2P_MAX_REG_CLASS_CHANNELS 20 + +/** + * struct p2p_channels - List of supported channels + */ +struct p2p_channels { + /** + * struct p2p_reg_class - Supported regulatory class + */ + struct p2p_reg_class { + /** + * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) + */ + u8 reg_class; + + /** + * channel - Supported channels + */ + u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; + + /** + * channels - Number of channel entries in use + */ + size_t channels; + } reg_class[P2P_MAX_REG_CLASSES]; + + /** + * reg_classes - Number of reg_class entries in use + */ + size_t reg_classes; +}; + +enum p2p_wps_method { + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC +}; + +/** + * struct p2p_go_neg_results - P2P Group Owner Negotiation results + */ +struct p2p_go_neg_results { + /** + * status - Negotiation result (Status Code) + * + * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate + * failed negotiation. + */ + int status; + + /** + * role_go - Whether local end is Group Owner + */ + int role_go; + + /** + * freq - Frequency of the group operational channel in MHz + */ + int freq; + + /** + * ssid - SSID of the group + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID in octets + */ + size_t ssid_len; + + /** + * passphrase - WPA2-Personal passphrase for the group (GO only) + */ + char passphrase[64]; + + /** + * peer_device_addr - P2P Device Address of the peer + */ + u8 peer_device_addr[ETH_ALEN]; + + /** + * peer_interface_addr - P2P Interface Address of the peer + */ + u8 peer_interface_addr[ETH_ALEN]; + + /** + * wps_method - WPS method to be used during provisioning + */ + enum p2p_wps_method wps_method; + +#define P2P_MAX_CHANNELS 50 + + /** + * freq_list - Zero-terminated list of possible operational channels + */ + int freq_list[P2P_MAX_CHANNELS]; + + /** + * persistent_group - Whether the group should be made persistent + * 0 = not persistent + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * peer_config_timeout - Peer configuration timeout (in 10 msec units) + */ + unsigned int peer_config_timeout; +}; + +struct p2p_data; + +enum p2p_scan_type { + P2P_SCAN_SOCIAL, + P2P_SCAN_FULL, + P2P_SCAN_SPECIFIC, + P2P_SCAN_SOCIAL_PLUS_ONE +}; + +#define P2P_MAX_WPS_VENDOR_EXT 10 + +/** + * struct p2p_peer_info - P2P peer information + */ +struct p2p_peer_info { + /** + * p2p_device_addr - P2P Device Address of the peer + */ + u8 p2p_device_addr[ETH_ALEN]; + + /** + * pri_dev_type - Primary Device Type + */ + u8 pri_dev_type[8]; + + /** + * device_name - Device Name (0..32 octets encoded in UTF-8) + */ + char device_name[33]; + + /** + * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) + */ + char manufacturer[65]; + + /** + * model_name - Model Name (0..32 octets encoded in UTF-8) + */ + char model_name[33]; + + /** + * model_number - Model Number (0..32 octets encoded in UTF-8) + */ + char model_number[33]; + + /** + * serial_number - Serial Number (0..32 octets encoded in UTF-8) + */ + char serial_number[33]; + + /** + * level - Signal level + */ + int level; + + /** + * config_methods - WPS Configuration Methods + */ + u16 config_methods; + + /** + * dev_capab - Device Capabilities + */ + u8 dev_capab; + + /** + * group_capab - Group Capabilities + */ + u8 group_capab; + + /** + * wps_sec_dev_type_list - WPS secondary device type list + * + * This list includes from 0 to 16 Secondary Device Types as indicated + * by wps_sec_dev_type_list_len (8 * number of types). + */ + u8 wps_sec_dev_type_list[128]; + + /** + * wps_sec_dev_type_list_len - Length of secondary device type list + */ + size_t wps_sec_dev_type_list_len; + + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; +}; + +enum p2p_prov_disc_status { + P2P_PROV_DISC_SUCCESS, + P2P_PROV_DISC_TIMEOUT, + P2P_PROV_DISC_REJECTED, +}; + +struct p2p_channel { + u8 op_class; + u8 chan; +}; + +/** + * struct p2p_config - P2P configuration + * + * This configuration is provided to the P2P module during initialization with + * p2p_init(). + */ +struct p2p_config { + /** + * country - Country code to use in P2P operations + */ + char country[3]; + + /** + * reg_class - Regulatory class for own listen channel + */ + u8 reg_class; + + /** + * channel - Own listen channel + */ + u8 channel; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * cfg_op_channel - Whether op_channel is hardcoded in configuration + */ + u8 cfg_op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + /** + * num_pref_chan - Number of pref_chan entries + */ + unsigned int num_pref_chan; + + /** + * pref_chan - Preferred channels for GO Negotiation + */ + struct p2p_channel *pref_chan; + + /** + * pri_dev_type - Primary Device Type (see WPS) + */ + u8 pri_dev_type[8]; + + /** + * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types + */ +#define P2P_SEC_DEVICE_TYPES 5 + + /** + * sec_dev_type - Optional secondary device types + */ + u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; + + /** + * num_sec_dev_types - Number of sec_dev_type entries + */ + size_t num_sec_dev_types; + + /** + * dev_addr - P2P Device Address + */ + u8 dev_addr[ETH_ALEN]; + + /** + * dev_name - Device Name + */ + char *dev_name; + + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + + u8 uuid[16]; + u16 config_methods; + + /** + * concurrent_operations - Whether concurrent operations are supported + */ + int concurrent_operations; + + /** + * max_peers - Maximum number of discovered peers to remember + * + * If more peers are discovered, older entries will be removed to make + * room for the new ones. + */ + size_t max_peers; + + /** + * p2p_intra_bss - Intra BSS communication is supported + */ + int p2p_intra_bss; + + /** + * ssid_postfix - Postfix data to add to the SSID + * + * This data will be added to the end of the SSID after the + * DIRECT- prefix. + */ + u8 ssid_postfix[32 - 9]; + + /** + * ssid_postfix_len - Length of the ssid_postfix data + */ + size_t ssid_postfix_len; + + /** + * msg_ctx - Context to use with wpa_msg() calls + */ + void *msg_ctx; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + + /* Callbacks to request lower layer driver operations */ + + /** + * p2p_scan - Request a P2P scan/search + * @ctx: Callback context from cb_ctx + * @type: Scan type + * @freq: Specific frequency (MHz) to scan or 0 for no restriction + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Array containing requested device types + * @dev_id: Device ID to search for or %NULL to find all devices + * Returns: 0 on success, -1 on failure + * + * This callback function is used to request a P2P scan or search + * operation to be completed. Type type argument specifies which type + * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the + * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL + * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC + * request a scan of a single channel specified by freq. + * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels + * plus one extra channel specified by freq. + * + * The full scan is used for the initial scan to find group owners from + * all. The other types are used during search phase scan of the social + * channels (with potential variation if the Listen channel of the + * target peer is known or if other channels are scanned in steps). + * + * The scan results are returned after this call by calling + * p2p_scan_res_handler() for each scan result that has a P2P IE and + * then calling p2p_scan_res_handled() to indicate that all scan + * results have been indicated. + */ + int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id); + + /** + * send_probe_resp - Transmit a Probe Response frame + * @ctx: Callback context from cb_ctx + * @buf: Probe Response frame (including the header and body) + * Returns: 0 on success, -1 on failure + * + * This function is used to reply to Probe Request frames that were + * indicated with a call to p2p_probe_req_rx(). The response is to be + * sent on the same channel or to be dropped if the driver is not + * anymore listening to Probe Request frames. + * + * Alternatively, the responsibility for building the Probe Response + * frames in Listen state may be in another system component in which + * case this function need to be implemented (i.e., the function + * pointer can be %NULL). The WPS and P2P IEs to be added for Probe + * Response frames in such a case are available from the + * start_listen() callback. It should be noted that the received Probe + * Request frames must be indicated by calling p2p_probe_req_rx() even + * if this send_probe_resp() is not used. + */ + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + + /** + * send_action - Transmit an Action frame + * @ctx: Callback context from cb_ctx + * @freq: Frequency in MHz for the channel on which to transmit + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @buf: Frame body (starting from Category field) + * @len: Length of buf in octets + * @wait_time: How many msec to wait for a response frame + * Returns: 0 on success, -1 on failure + * + * The Action frame may not be transmitted immediately and the status + * of the transmission must be reported by calling + * p2p_send_action_cb() once the frame has either been transmitted or + * it has been dropped due to excessive retries or other failure to + * transmit. + */ + int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); + + /** + * send_action_done - Notify that Action frame sequence was completed + * @ctx: Callback context from cb_ctx + * + * This function is called when the Action frame sequence that was + * started with send_action() has been completed, i.e., when there is + * no need to wait for a response from the destination peer anymore. + */ + void (*send_action_done)(void *ctx); + + /** + * start_listen - Start Listen state + * @ctx: Callback context from cb_ctx + * @freq: Frequency of the listen channel in MHz + * @duration: Duration for the Listen state in milliseconds + * @probe_resp_ie: IE(s) to be added to Probe Response frames + * Returns: 0 on success, -1 on failure + * + * This Listen state may not start immediately since the driver may + * have other pending operations to complete first. Once the Listen + * state has started, p2p_listen_cb() must be called to notify the P2P + * module. Once the Listen state is stopped, p2p_listen_end() must be + * called to notify the P2P module that the driver is not in the Listen + * state anymore. + * + * If the send_probe_resp() is not used for generating the response, + * the IEs from probe_resp_ie need to be added to the end of the Probe + * Response frame body. If send_probe_resp() is used, the probe_resp_ie + * information can be ignored. + */ + int (*start_listen)(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie); + /** + * stop_listen - Stop Listen state + * @ctx: Callback context from cb_ctx + * + * This callback can be used to stop a Listen state operation that was + * previously requested with start_listen(). + */ + void (*stop_listen)(void *ctx); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @ctx: Callback context from cb_ctx + * @interface_addr: P2P Interface Address of the GO + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len); + + /* Callbacks to notify events to upper layer management entity */ + + /** + * dev_found - Notification of a found P2P Device + * @ctx: Callback context from cb_ctx + * @addr: Source address of the message triggering this notification + * @info: P2P peer information + * @new_device: Inform if the peer is newly found + * + * This callback is used to notify that a new P2P Device has been + * found. This may happen, e.g., during Search state based on scan + * results or during Listen state based on receive Probe Request and + * Group Owner Negotiation Request. + */ + void (*dev_found)(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); + + /** + * dev_lost - Notification of a lost P2P Device + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of the lost P2P Device + * + * This callback is used to notify that a P2P Device has been deleted. + */ + void (*dev_lost)(void *ctx, const u8 *dev_addr); + + /** + * go_neg_req_rx - Notification of a receive GO Negotiation Request + * @ctx: Callback context from cb_ctx + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password ID + * + * This callback is used to notify that a P2P Device is requesting + * group owner negotiation with us, but we do not have all the + * necessary information to start GO Negotiation. This indicates that + * the local user has not authorized the connection yet by providing a + * PIN or PBC button press. This information can be provided with a + * call to p2p_connect(). + */ + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + + /** + * go_neg_completed - Notification of GO Negotiation results + * @ctx: Callback context from cb_ctx + * @res: GO Negotiation results + * + * This callback is used to notify that Group Owner Negotiation has + * been completed. Non-zero struct p2p_go_neg_results::status indicates + * failed negotiation. In case of success, this function is responsible + * for creating a new group interface (or using the existing interface + * depending on driver features), setting up the group interface in + * proper mode based on struct p2p_go_neg_results::role_go and + * initializing WPS provisioning either as a Registrar (if GO) or as an + * Enrollee. Successful WPS provisioning must be indicated by calling + * p2p_wps_success_cb(). The callee is responsible for timing out group + * formation if WPS provisioning cannot be completed successfully + * within 15 seconds. + */ + void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); + + /** + * sd_request - Callback on Service Discovery Request + * @ctx: Callback context from cb_ctx + * @freq: Frequency (in MHz) of the channel + * @sa: Source address of the request + * @dialog_token: Dialog token + * @update_indic: Service Update Indicator from the source of request + * @tlvs: P2P Service Request TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * request. Response to the query must be indicated by calling + * p2p_sd_response() with the context information from the arguments to + * this callback function. + * + * This callback handler can be set to %NULL to indicate that service + * discovery is not supported. + */ + void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); + + /** + * sd_response - Callback on Service Discovery Response + * @ctx: Callback context from cb_ctx + * @sa: Source address of the request + * @update_indic: Service Update Indicator from the source of response + * @tlvs: P2P Service Response TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * response. This callback handler can be set to %NULL if no service + * discovery requests are used. The information provided with this call + * is replies to the queries scheduled with p2p_sd_request(). + */ + void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); + + /** + * prov_disc_req - Callback on Provisiong Discovery Request + * @ctx: Callback context from cb_ctx + * @peer: Source address of the request + * @config_methods: Requested WPS Config Method + * @dev_addr: P2P Device Address of the found P2P Device + * @pri_dev_type: Primary Device Type + * @dev_name: Device Name + * @supp_config_methods: Supported configuration Methods + * @dev_capab: Device Capabilities + * @group_capab: Group Capabilities + * @group_id: P2P Group ID (or %NULL if not included) + * @group_id_len: Length of P2P Group ID + * + * This callback is used to indicate reception of a Provision Discovery + * Request frame that the P2P module accepted. + */ + void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len); + + /** + * prov_disc_resp - Callback on Provisiong Discovery Response + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @config_methods: Value from p2p_prov_disc_req() or 0 on failure + * + * This callback is used to indicate reception of a Provision Discovery + * Response frame for a pending request scheduled with + * p2p_prov_disc_req(). This callback handler can be set to %NULL if + * provision discovery is not used. + */ + void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); + + /** + * prov_disc_fail - Callback on Provision Discovery failure + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS + * + * This callback is used to indicate either a failure or no response + * to an earlier provision discovery request. + * + * This callback handler can be set to %NULL if provision discovery + * is not used or failures do not need to be indicated. + */ + void (*prov_disc_fail)(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status); + + /** + * invitation_process - Optional callback for processing Invitations + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID from the request or %NULL if not included + * @go_dev_addr: GO Device Address from P2P Group ID + * @ssid: SSID from P2P Group ID + * @ssid_len: Length of ssid buffer in octets + * @go: Variable for returning whether the local end is GO in the group + * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) + * @force_freq: Variable for returning forced frequency for the group + * @persistent_group: Whether this is an invitation to reinvoke a + * persistent group (instead of invitation to join an active + * group) + * Returns: Status code (P2P_SC_*) + * + * This optional callback can be used to implement persistent reconnect + * by allowing automatic restarting of persistent groups without user + * interaction. If this callback is not implemented (i.e., is %NULL), + * the received Invitation Request frames are replied with + * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the + * invitation_result() callback. + * + * If the requested parameters are acceptable and the group is known, + * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, + * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED + * can be returned if there is not enough data to provide immediate + * response, i.e., if some sort of user interaction is needed. The + * invitation_received() callback will be called in that case + * immediately after this call. + */ + u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *go_dev_addr, const u8 *ssid, + size_t ssid_len, int *go, u8 *group_bssid, + int *force_freq, int persistent_group); + + /** + * invitation_received - Callback on Invitation Request RX + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID or %NULL if not received + * @ssid: SSID of the group + * @ssid_len: Length of ssid in octets + * @go_dev_addr: GO Device Address + * @status: Response Status + * @op_freq: Operational frequency for the group + * + * This callback is used to indicate sending of an Invitation Response + * for a received Invitation Request. If status == 0 (success), the + * upper layer code is responsible for starting the group. status == 1 + * indicates need to get user authorization for the group. Other status + * values indicate that the invitation request was rejected. + */ + void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq); + + /** + * invitation_result - Callback on Invitation result + * @ctx: Callback context from cb_ctx + * @status: Negotiation result (Status Code) + * @bssid: P2P Group BSSID or %NULL if not received + * + * This callback is used to indicate result of an Invitation procedure + * started with a call to p2p_invite(). The indicated status code is + * the value received from the peer in Invitation Response with 0 + * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a + * local failure in transmitting the Invitation Request. + */ + void (*invitation_result)(void *ctx, int status, const u8 *bssid); + + /** + * go_connected - Check whether we are connected to a GO + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of a GO + * Returns: 1 if we are connected as a P2P client to the specified GO + * or 0 if not. + */ + int (*go_connected)(void *ctx, const u8 *dev_addr); +}; + + +/* P2P module initialization/deinitialization */ + +/** + * p2p_init - Initialize P2P module + * @cfg: P2P module configuration + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize global P2P module context (one per + * device). The P2P module will keep a copy of the configuration data, so the + * caller does not need to maintain this structure. However, the callback + * functions and the context parameters to them must be kept available until + * the P2P module is deinitialized with p2p_deinit(). + */ +struct p2p_data * p2p_init(const struct p2p_config *cfg); + +/** + * p2p_deinit - Deinitialize P2P module + * @p2p: P2P module context from p2p_init() + */ +void p2p_deinit(struct p2p_data *p2p); + +/** + * p2p_flush - Flush P2P module state + * @p2p: P2P module context from p2p_init() + * + * This command removes the P2P module state like peer device entries. + */ +void p2p_flush(struct p2p_data *p2p); + +/** + * p2p_unauthorize - Unauthorize the specified peer device + * @p2p: P2P module context from p2p_init() + * @addr: P2P peer entry to be unauthorized + * Returns: 0 on success, -1 on failure + * + * This command removes any connection authorization from the specified P2P + * peer device address. This can be used, e.g., to cancel effect of a previous + * p2p_authorize() or p2p_connect() call that has not yet resulted in completed + * GO Negotiation. + */ +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_dev_name - Set device name + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); + +/** + * p2p_set_pri_dev_type - Set primary device type + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); + +/** + * p2p_set_sec_dev_types - Set secondary device types + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types); + +int p2p_set_country(struct p2p_data *p2p, const char *country); + + +/* Commands from upper layer management entity */ + +enum p2p_discovery_type { + P2P_FIND_START_WITH_FULL, + P2P_FIND_ONLY_SOCIAL, + P2P_FIND_PROGRESSIVE +}; + +/** + * p2p_find - Start P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types array, must be an array + * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no + * requested device types. + * @dev_id: Device ID to search for or %NULL to find all devices + * Returns: 0 on success, -1 on failure + */ +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id); + +/** + * p2p_stop_find - Stop P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_find(struct p2p_data *p2p); + +/** + * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq + * @p2p: P2P module context from p2p_init() + * @freq: Frequency in MHz for next operation + * + * This is like p2p_stop_find(), but Listen state is not stopped if we are + * already on the same frequency. + */ +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); + +/** + * p2p_listen - Start P2P Listen state for specified duration + * @p2p: P2P module context from p2p_init() + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the device + * discoverable on the listen channel for an extended set of time. At least in + * its current form, this is mainly used for testing purposes and may not be of + * much use for normal P2P operations. + */ +int p2p_listen(struct p2p_data *p2p, unsigned int timeout); + +/** + * p2p_connect - Start P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * Returns: 0 on success, -1 on failure + */ +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group); + +/** + * p2p_authorize - Authorize P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * Returns: 0 on success, -1 on failure + * + * This is like p2p_connect(), but the actual group negotiation is not + * initiated automatically, i.e., the other end is expected to do that. + */ +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group); + +/** + * p2p_reject - Reject peer device (explicitly block connection attempts) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * Returns: 0 on success, -1 on failure + */ +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); + +/** + * p2p_prov_disc_req - Send Provision Discovery Request + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * @join: Whether this is used by a client joining an active group + * @force_freq: Forced TX frequency for the frame (mainly for the join case) + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to display a PIN + * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us + * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame + * is transmitted once immediately and if no response is received, the frame + * will be sent again whenever the target device is discovered during device + * dsicovery (start with a p2p_find() call). Response from the peer is + * indicated with the p2p_config::prov_disc_resp() callback. + */ +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq); + +/** + * p2p_sd_request - Schedule a service discovery query + * @p2p: P2P module context from p2p_init() + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or %NULL on failure + * + * Response to the query is indicated with the p2p_config::sd_response() + * callback. + */ +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); + +/** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @p2p: P2P module context from p2p_init() + * @req: Query reference from p2p_sd_request() + * Returns: 0 if request for cancelled; -1 if not found + */ +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); + +/** + * p2p_sd_response - Send response to a service discovery query + * @p2p: P2P module context from p2p_init() + * @freq: Frequency from p2p_config::sd_request() callback + * @dst: Destination address from p2p_config::sd_request() callback + * @dialog_token: Dialog token from p2p_config::sd_request() callback + * @resp_tlvs: P2P Service Response TLV(s) + * + * This function is called as a response to the request indicated with + * p2p_config::sd_request() callback. + */ +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs); + +/** + * p2p_sd_service_update - Indicate a change in local services + * @p2p: P2P module context from p2p_init() + * + * This function needs to be called whenever there is a change in availability + * of the local services. This will increment the Service Update Indicator + * value which will be used in SD Request and Response frames. + */ +void p2p_sd_service_update(struct p2p_data *p2p); + + +enum p2p_invite_role { + P2P_INVITE_ROLE_GO, + P2P_INVITE_ROLE_ACTIVE_GO, + P2P_INVITE_ROLE_CLIENT +}; + +/** + * p2p_invite - Invite a P2P Device into a group + * @p2p: P2P module context from p2p_init() + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @force_freq: The only allowed channel frequency in MHz or 0 + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * Returns: 0 on success, -1 on failure + */ +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group); + +/** + * p2p_presence_req - Request GO presence + * @p2p: P2P module context from p2p_init() + * @go_interface_addr: GO P2P Interface Address + * @own_interface_addr: Own P2P Interface Address for this group + * @freq: Group operating frequence (in MHz) + * @duration1: Preferred presence duration in microseconds + * @interval1: Preferred presence interval in microseconds + * @duration2: Acceptable presence duration in microseconds + * @interval2: Acceptable presence interval in microseconds + * Returns: 0 on success, -1 on failure + * + * If both duration and interval values are zero, the parameter pair is not + * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). + */ +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2); + +/** + * p2p_ext_listen - Set Extended Listen Timing + * @p2p: P2P module context from p2p_init() + * @freq: Group operating frequence (in MHz) + * @period: Availability period in milliseconds (1-65535; 0 to disable) + * @interval: Availability interval in milliseconds (1-65535; 0 to disable) + * Returns: 0 on success, -1 on failure + * + * This function can be used to enable or disable (period = interval = 0) + * Extended Listen Timing. When enabled, the P2P Device will become + * discoverable (go into Listen State) every @interval milliseconds for at + * least @period milliseconds. + */ +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval); + +/* Event notifications from upper layer management operations */ + +/** + * p2p_wps_success_cb - Report successfully completed WPS provisioning + * @p2p: P2P module context from p2p_init() + * @mac_addr: Peer address + * + * This function is used to report successfully completed WPS provisioning + * during group formation in both GO/Registrar and client/Enrollee roles. + */ +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); + +/** + * p2p_group_formation_failed - Report failed WPS provisioning + * @p2p: P2P module context from p2p_init() + * + * This function is used to report failed group formation. This can happen + * either due to failed WPS provisioning or due to 15 second timeout during + * the provisioning phase. + */ +void p2p_group_formation_failed(struct p2p_data *p2p); + +/** + * p2p_get_provisioning_info - Get any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @addr: Peer P2P Device Address + * Returns: WPS provisioning information (WPS config method) or 0 if no + * information is available + * + * This function is used to retrieve stored WPS provisioning info for the given + * peer. + */ +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_clear_provisioning_info - Clear any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @iface_addr: Peer P2P Device Address + * + * This function is used to clear stored WPS provisioning info for the given + * peer. + */ +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); + + +/* Event notifications from lower layer driver operations */ + +/** + * p2p_probe_req_rx - Report reception of a Probe Request frame + * @p2p: P2P module context from p2p_init() + * @addr: Source MAC address + * @dst: Destination MAC address if available or %NULL + * @bssid: BSSID if available or %NULL + * @ie: Information elements from the Probe Request frame body + * @ie_len: Length of ie buffer in octets + * Returns: 0 to indicate the frame was not processed or 1 if it was + */ +int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len); + +/** + * p2p_rx_action - Report received Action frame + * @p2p: P2P module context from p2p_init() + * @da: Destination address of the received Action frame + * @sa: Source address of the received Action frame + * @bssid: Address 3 of the received Action frame + * @category: Category of the received Action frame + * @data: Action frame body after the Category field + * @len: Length of the data buffer in octets + * @freq: Frequency (in MHz) on which the frame was received + */ +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq); + +/** + * p2p_scan_res_handler - Indicate a P2P scan results + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID of the scan result + * @freq: Frequency of the channel on which the device was found in MHz + * @level: Signal level (signal strength of the received Beacon/Probe Response + * frame) + * @ies: Pointer to IEs from the scan result + * @ies_len: Length of the ies buffer + * Returns: 0 to continue or 1 to stop scan result indication + * + * This function is called to indicate a scan result entry with P2P IE from a + * scan requested with struct p2p_config::p2p_scan(). This can be called during + * the actual scan process (i.e., whenever a new device is found) or as a + * sequence of calls after the full scan has been completed. The former option + * can result in optimized operations, but may not be supported by all + * driver/firmware designs. The ies buffer need to include at least the P2P IE, + * but it is recommended to include all IEs received from the device. The + * caller does not need to check that the IEs contain a P2P IE before calling + * this function since frames will be filtered internally if needed. + * + * This function will return 1 if it wants to stop scan result iteration (and + * scan in general if it is still in progress). This is used to allow faster + * start of a pending operation, e.g., to start a pending GO negotiation. + */ +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + int level, const u8 *ies, size_t ies_len); + +/** + * p2p_scan_res_handled - Indicate end of scan results + * @p2p: P2P module context from p2p_init() + * + * This function is called to indicate that all P2P scan results from a scan + * have been reported with zero or more calls to p2p_scan_res_handler(). This + * function must be called as a response to successful + * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() + * calls stopped iteration. + */ +void p2p_scan_res_handled(struct p2p_data *p2p); + +enum p2p_send_action_result { + P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, + P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ +}; + +/** + * p2p_send_action_cb - Notify TX status of an Action frame + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @result: Result of the transmission attempt + * + * This function is used to indicate the result of an Action frame transmission + * that was requested with struct p2p_config::send_action() callback. + */ +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result); + +/** + * p2p_listen_cb - Indicate the start of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * @duration: Duration for the Listen state in milliseconds + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has started. + */ +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration); + +/** + * p2p_listen_end - Indicate the end of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * Returns: 0 if no operations were started, 1 if an operation was started + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has ended. + */ +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + + +/* Per-group P2P state for GO */ + +struct p2p_group; + +/** + * struct p2p_group_config - P2P group configuration + * + * This configuration is provided to the P2P module during initialization of + * the per-group information with p2p_group_init(). + */ +struct p2p_group_config { + /** + * persistent_group - Whether the group is persistent + * 0 = not a persistent group + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * interface_addr - P2P Interface Address of the group + */ + u8 interface_addr[ETH_ALEN]; + + /** + * max_clients - Maximum number of clients in the group + */ + unsigned int max_clients; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * ie_update - Notification of IE update + * @ctx: Callback context from cb_ctx + * @beacon_ies: P2P IE for Beacon frames or %NULL if no change + * @proberesp_ies: P2P Ie for Probe Response frames + * + * P2P module uses this callback function to notify whenever the P2P IE + * in Beacon or Probe Response frames should be updated based on group + * events. + * + * The callee is responsible for freeing the returned buffer(s) with + * wpabuf_free(). + */ + void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies); + + /** + * idle_update - Notification of changes in group idle state + * @ctx: Callback context from cb_ctx + * @idle: Whether the group is idle (no associated stations) + */ + void (*idle_update)(void *ctx, int idle); +}; + +/** + * p2p_group_init - Initialize P2P group + * @p2p: P2P module context from p2p_init() + * @config: P2P group configuration (will be freed by p2p_group_deinit()) + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize per-group P2P module context. Currently, + * this is only used to manage GO functionality and P2P clients do not need to + * create an instance of this per-group information. + */ +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config); + +/** + * p2p_group_deinit - Deinitialize P2P group + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_deinit(struct p2p_group *group); + +/** + * p2p_group_notif_assoc - Notification of P2P client association with GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + * @ie: IEs from the (Re)association Request frame + * @len: Length of the ie buffer in octets + * Returns: 0 on success, -1 on failure + */ +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len); + +/** + * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response + * @group: P2P group context from p2p_group_init() + * @status: Status value (P2P_SC_SUCCESS if association succeeded) + * Returns: P2P IE for (Re)association Response or %NULL on failure + * + * The caller is responsible for freeing the returned buffer with + * wpabuf_free(). + */ +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); + +/** + * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + */ +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_notif_formation_done - Notification of completed group formation + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_notif_formation_done(struct p2p_group *group); + +/** + * p2p_group_notif_noa - Notification of NoA change + * @group: P2P group context from p2p_group_init() + * @noa: Notice of Absence attribute payload, %NULL if none + * @noa_len: Length of noa buffer in octets + * Returns: 0 on success, -1 on failure + * + * Notify the P2P group management about a new NoA contents. This will be + * inserted into the P2P IEs in Beacon and Probe Response frames with rest of + * the group information. + */ +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len); + +/** + * p2p_group_match_dev_type - Match device types in group with requested type + * @group: P2P group context from p2p_group_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. Match will be reported if the WPS IE + * is not requested any specific device type. + */ +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); + +/** + * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id + */ +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); + +/** + * p2p_group_go_discover - Send GO Discoverability Request to a group client + * @group: P2P group context from p2p_group_init() + * Returns: 0 on success (frame scheduled); -1 if client was not found + */ +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq); + + +/* Generic helper functions */ + +/** + * p2p_ie_text - Build text format description of P2P IE + * @p2p_ie: P2P IE + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); + +/** + * p2p_scan_result_text - Build text format description of P2P IE + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); + +/** + * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); + +/** + * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID + * @buf: Buffer for writing the P2P IE + * @len: Maximum buf length in octets + * @p2p_group: Whether this is for association with a P2P GO + * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none + * Returns: Number of octets written into buf or -1 on failure + */ +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie); + +/** + * p2p_scan_ie - Build P2P IE for Probe Request + * @p2p: P2P module context from p2p_init() + * @ies: Buffer for writing P2P IE + * @dev_id: Device ID to search for or %NULL for any + */ +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); + +/** + * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie + * @p2p: P2P module context from p2p_init() + * Returns: Number of octets that p2p_scan_ie() may add to the buffer + */ +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); + +/** + * p2p_go_params - Generate random P2P group parameters + * @p2p: P2P module context from p2p_init() + * @params: Buffer for parameters + * Returns: 0 on success, -1 on failure + */ +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); + +/** + * p2p_get_group_capab - Get Group Capability from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Group Capability + */ +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); + +/** + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection + * @p2p_ie: P2P IE(s) contents + * Returns: 0 if cross connection is allow, 1 if not + */ +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); + +/** + * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Pointer to P2P Device Address or %NULL if not included + */ +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); + +/** + * p2p_get_peer_info - Get P2P peer information + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: Pointer to peer info or %NULL if not found + */ +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next); + +/** + * p2p_get_peer_info_txt - Get internal P2P peer information in text format + * @info: Pointer to P2P peer info from p2p_get_peer_info() + * @buf: Buffer for returning text + * @buflen: Maximum buffer length + * Returns: Number of octets written to the buffer or -1 on failure + * + * Note: This information is internal to the P2P module and subject to change. + * As such, this should not really be used by external programs for purposes + * other than debugging. + */ +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen); + +/** + * p2p_peer_known - Check whether P2P peer is known + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: 1 if the specified device is in the P2P peer table or 0 if not + */ +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_client_discoverability - Set client discoverability capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether client discoverability will be enabled + * + * This function can be used to disable (and re-enable) client discoverability. + * This capability is enabled by default and should not be disabled in normal + * use cases, i.e., this is mainly for testing purposes. + */ +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); + +/** + * p2p_set_managed_oper - Set managed P2P Device operations capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether managed P2P Device operations will be enabled + */ +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr); +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr); + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_cross_connect - Set cross connection capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether cross connection will be enabled + */ +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); + +/** + * p2p_set_intra_bss_dist - Set intra BSS distribution + * @p2p: P2P module context from p2p_init() + * @enabled: Whether intra BSS distribution will be enabled + */ +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); + +/** + * p2p_supported_freq - Check whether channel is supported for P2P + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); + +void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); + +/** + * p2p_set_best_channels - Update best channel information + * @p2p: P2P module context from p2p_init() + * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band + * @freq_5: Frequency (MHz) of best channel in 5 GHz band + * @freq_overall: Frequency (MHz) of best channel overall + */ +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall); + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); + +/** + * p2p_get_group_num_members - Get number of members in group + * @group: P2P group context from p2p_group_init() + * Returns: Number of members in the group + */ +unsigned int p2p_get_group_num_members(struct p2p_group *group); + +/** + * p2p_iterate_group_members - Iterate group members + * @group: P2P group context from p2p_group_init() + * @next: iteration pointer, must be a pointer to a void * that is set to %NULL + * on the first call and not modified later + * Returns: A P2P Interface Address for each call and %NULL for no more members + */ +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); + +/** + * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @addr: P2P Interface Address of the client + * Returns: P2P Device Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_is_client_connected - Check whether a specific client is connected + * @group: P2P group context from p2p_group_init() + * @addr: P2P Device Address of the client + * Returns: 1 if client is connected or 0 if not + */ +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); + +/** + * p2p_get_peer_found - Get P2P peer info structure of a found peer + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: The first P2P peer info available or %NULL if no such peer exists + */ +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); + +/** + * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions + * @p2p: P2P module context from p2p_init() + */ +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); + +/** + * p2p_add_wps_vendor_extension - Add a WPS vendor extension + * @p2p: P2P module context from p2p_init() + * @vendor_ext: The vendor extensions to add + * Returns: 0 on success, -1 on failure + * + * The wpabuf structures in the array are owned by the P2P + * module after this call. + */ +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext); + +/** + * p2p_set_oper_channel - Set the P2P operating channel + * @p2p: P2P module context from p2p_init() + * @op_reg_class: Operating regulatory class to set + * @op_channel: operating channel to set + * @cfg_op_channel : Whether op_channel is hardcoded in configuration + * Returns: 0 on success, -1 on failure + */ +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel); + +/** + * p2p_set_pref_chan - Set P2P preferred channel list + * @p2p: P2P module context from p2p_init() + * @num_pref_chan: Number of entries in pref_chan list + * @pref_chan: Preferred channels or %NULL to remove preferences + * Returns: 0 on success, -1 on failure + */ +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan); + +/** + * p2p_in_progress - Check whether a P2P operation is progress + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle or 1 if an operation is in progress + */ +int p2p_in_progress(struct p2p_data *p2p); + +/** + * p2p_other_scan_completed - Notify completion of non-P2P scan + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle or 1 if an operation was started + */ +int p2p_other_scan_completed(struct p2p_data *p2p); + +const char * p2p_wps_method_text(enum p2p_wps_method method); + +#endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c new file mode 100644 index 0000000..a82e16d --- /dev/null +++ b/src/p2p/p2p_build.c @@ -0,0 +1,433 @@ +/* + * P2P - IE builder + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_i.h" +#include "p2p_i.h" + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) +{ + u8 *len; + + /* P2P IE header */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); /* IE length to be filled */ + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); + return len; +} + + +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len) +{ + /* Update P2P IE Length */ + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; +} + + +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab) +{ + /* P2P Capability */ + wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */ + wpabuf_put_u8(buf, group_capab); /* Group Capabilities */ + wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x", + dev_capab, group_capab); +} + + +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent) +{ + /* Group Owner Intent */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, go_intent); + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u", + go_intent >> 1, go_intent & 0x01); +} + + +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Listen Channel */ + wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Operating Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan) +{ + u8 *len; + size_t i; + + /* Channel List */ + wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + wpabuf_put_data(buf, country, 3); /* Country String */ + + for (i = 0; i < chan->reg_classes; i++) { + struct p2p_reg_class *c = &chan->reg_class[i]; + wpabuf_put_u8(buf, c->reg_class); + wpabuf_put_u8(buf, c->channels); + wpabuf_put_data(buf, c->channel, c->channels); + } + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_printf(MSG_DEBUG, "P2P: * Channel List"); +} + + +void p2p_buf_add_status(struct wpabuf *buf, u8 status) +{ + /* Status */ + wpabuf_put_u8(buf, P2P_ATTR_STATUS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, status); + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status); +} + + +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer) +{ + u8 *len; + u16 methods; + size_t nlen, i; + + /* P2P Device Info */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + + /* Config Methods */ + methods = 0; + if (peer && peer->wps_method != WPS_NOT_READY) { + if (peer->wps_method == WPS_PBC) + methods |= WPS_CONFIG_PUSHBUTTON; + else if (peer->wps_method == WPS_PIN_DISPLAY || + peer->wps_method == WPS_PIN_KEYPAD) + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } else if (p2p->cfg->config_methods) { + methods |= p2p->cfg->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | + WPS_CONFIG_KEYPAD); + } else { + methods |= WPS_CONFIG_PUSHBUTTON; + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } + wpabuf_put_be16(buf, methods); + + /* Primary Device Type */ + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, + sizeof(p2p->cfg->pri_dev_type)); + + /* Number of Secondary Device Types */ + wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types); + + /* Secondary Device Type List */ + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i], + WPS_DEV_TYPE_LEN); + + /* Device Name */ + nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0; + wpabuf_put_be16(buf, ATTR_DEV_NAME); + wpabuf_put_be16(buf, nlen); + wpabuf_put_data(buf, p2p->cfg->dev_name, nlen); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_printf(MSG_DEBUG, "P2P: * Device Info"); +} + + +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr) +{ + /* P2P Device ID */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr)); +} + + +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout) +{ + /* Configuration Timeout */ + wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, go_timeout); + wpabuf_put_u8(buf, client_timeout); + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) " + "client %d (*10ms)", go_timeout, client_timeout); +} + + +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr) +{ + /* Intended P2P Interface Address */ + wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, interface_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR, + MAC2STR(interface_addr)); +} + + +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid) +{ + /* P2P Group BSSID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, bssid, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR, + MAC2STR(bssid)); +} + + +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len) +{ + /* P2P Group ID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID); + wpabuf_put_le16(buf, ETH_ALEN + ssid_len); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpabuf_put_data(buf, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, + MAC2STR(dev_addr)); +} + + +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags) +{ + /* Invitation Flags */ + wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, flags); + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags); +} + + +static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc) +{ + if (desc == NULL) + return; + + wpabuf_put_u8(buf, desc->count_type); + wpabuf_put_le32(buf, desc->duration); + wpabuf_put_le32(buf, desc->interval); + wpabuf_put_le32(buf, desc->start_time); +} + + +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2) +{ + /* Notice of Absence */ + wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0)); + wpabuf_put_u8(buf, noa_index); + wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f)); + p2p_buf_add_noa_desc(buf, desc1); + p2p_buf_add_noa_desc(buf, desc2); + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); +} + + +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval) +{ + /* Extended Listen Timing */ + wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING); + wpabuf_put_le16(buf, 4); + wpabuf_put_le16(buf, period); + wpabuf_put_le16(buf, interval); + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec " + "interval %u msec)", period, interval); +} + + +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) +{ + /* P2P Interface */ + wpabuf_put_u8(buf, P2P_ATTR_INTERFACE); + wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN); + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + /* + * FIX: Fetch interface address list from driver. Do not include + * the P2P Device address if it is never used as interface address. + */ + /* P2P Interface Address Count */ + wpabuf_put_u8(buf, 1); + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); +} + + +static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, + const char *val) +{ + size_t len; + + wpabuf_put_be16(buf, attr); + len = val ? os_strlen(val) : 0; +#ifndef CONFIG_WPS_STRICT + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zeor-length + * attributes. As a workaround, send a space character if the + * device attribute string is empty. + */ + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, ' '); + return; + } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(buf, len); + if (val) + wpabuf_put_data(buf, val, len); +} + + +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, + int all_attr) +{ + u8 *len; + int i; + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + wps_build_version(buf); + + if (all_attr) { + wpabuf_put_be16(buf, ATTR_WPS_STATE); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); + } + + /* Device Password ID */ + wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(buf, 2); + wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id); + wpabuf_put_be16(buf, pw_id); + + if (all_attr) { + wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); + + wps_build_uuid_e(buf, p2p->cfg->uuid); + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer); + p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name); + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number); + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number); + + wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); + + p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name); + + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, p2p->cfg->config_methods); + } + + wps_build_wfa_ext(buf, 0, NULL, 0); + + if (all_attr && p2p->cfg->num_sec_dev_types) { + wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + wpabuf_put_data(buf, p2p->cfg->sec_dev_type, + WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + } + + /* Add the WPS vendor extensions */ + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + if (wpabuf_tailroom(buf) < + 4 + wpabuf_len(p2p->wps_vendor_ext[i])) + continue; + wpabuf_put_be16(buf, ATTR_VENDOR_EXT); + wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i])); + wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]); + } + + p2p_buf_update_ie_hdr(buf, len); +} diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c new file mode 100644 index 0000000..47cc0fd --- /dev/null +++ b/src/p2p/p2p_dev_disc.c @@ -0,0 +1,365 @@ +/* + * Wi-Fi Direct - P2P Device Discoverability procedure + * Copyright (c) 2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, + struct p2p_device *go, + const u8 *dev_id) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + go->dialog_token++; + if (go->dialog_token == 0) + go->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_device_id(buf, dev_id); + p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, + go->oper_ssid_len); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Request TX callback: success=%d", + success); + + if (!success) { + /* + * Use P2P find, if needed, to find the other device or to + * retry device discoverability. + */ + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 100000); + return; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO acknowledged Device Discoverability Request - wait " + "for response"); + /* + * TODO: is the remain-on-channel from Action frame TX long enough for + * most cases or should we try to increase its duration and/or start + * another remain-on-channel if needed once the previous one expires? + */ +} + + +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct p2p_device *go; + struct wpabuf *req; + + go = p2p_get_device(p2p, dev->member_in_go_dev); + if (go == NULL || dev->oper_freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Could not find peer entry for GO and frequency " + "to send Device Discoverability Request"); + return -1; + } + + req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr); + if (req == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Device Discoverability Request to GO " MACSTR + " for client " MACSTR, + MAC2STR(go->info.p2p_device_addr), + MAC2STR(dev->info.p2p_device_addr)); + + p2p->pending_client_disc_go = go; + os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, + ETH_ALEN); + p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; + if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, + p2p->cfg->dev_addr, go->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 1000) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + wpabuf_free(req); + /* TODO: how to recover from failure? */ + return -1; + } + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Response TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); +} + + +static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, + const u8 *addr, int freq, u8 status) +{ + struct wpabuf *resp; + + resp = p2p_build_dev_disc_resp(dialog_token, status); + if (resp == NULL) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Device Discoverability Response to " MACSTR + " (status %u freq %d)", + MAC2STR(addr), status, freq); + + p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE; + if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + size_t g; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Device Discoverability Request from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.dialog_token == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid Dialog Token 0 (must be nonzero) in " + "Device Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + if (msg.device_id == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: P2P Device ID attribute missing from Device " + "Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, + rx_freq) == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " + "GO Discoverability Request for the target " + "device"); + /* + * P2P group code will use a callback to indicate TX + * status, so that we can reply to the request once the + * target client has acknowledged the request or it has + * timed out. + */ + p2p->pending_dev_disc_dialog_token = msg.dialog_token; + os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN); + p2p->pending_dev_disc_freq = rx_freq; + p2p_parse_free(&msg); + return; + } + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " + "was not found in any group or did not support client " + "discoverability"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + p2p_parse_free(&msg); +} + + +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *go; + u8 status; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Device Discoverability Response from " MACSTR, + MAC2STR(sa)); + + go = p2p->pending_client_disc_go; + if (go == NULL || + os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " + "Device Discoverability Response"); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.status == NULL) { + p2p_parse_free(&msg); + return; + } + + if (msg.dialog_token != go->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " + "Discoverability Response with unexpected dialog " + "token %u (expected %u)", + msg.dialog_token, go->dialog_token); + p2p_parse_free(&msg); + return; + } + + status = *msg.status; + p2p_parse_free(&msg); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Device Discoverability Response status %u", status); + + if (p2p->go_neg_peer == NULL || + os_memcmp(p2p->pending_client_disc_addr, + p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || + os_memcmp(p2p->go_neg_peer->member_in_go_dev, + go->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " + "operation with the client discoverability peer " + "anymore"); + return; + } + + if (status == 0) { + /* + * Peer is expected to be awake for at least 100 TU; try to + * connect immediately. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability request succeeded"); + if (p2p->state == P2P_CONNECT) { + /* + * Change state to force the timeout to start in + * P2P_CONNECT again without going through the short + * Listen state. + */ + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + p2p_set_timeout(p2p, 0, 0); + } else { + /* + * Client discoverability request failed; try to connect from + * timeout. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Client discoverability request failed"); + p2p_set_timeout(p2p, 0, 500000); + } + +} + + +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Discoverability Request TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (p2p->pending_dev_disc_dialog_token == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " + "Discoverability Request"); + return; + } + + p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token, + p2p->pending_dev_disc_addr, + p2p->pending_dev_disc_freq, + success ? P2P_SC_SUCCESS : + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + + p2p->pending_dev_disc_dialog_token = 0; +} + + +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + unsigned int tu; + struct wpabuf *ies; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Discoverability Request - remain awake for " + "100 TU"); + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + /* Remain awake 100 TU on operating channel */ + p2p->pending_client_disc_freq = rx_freq; + tu = 100; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, + ies) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to start listen mode for client " + "discoverability"); + } + wpabuf_free(ies); +} diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c new file mode 100644 index 0000000..f83d3de --- /dev/null +++ b/src/p2p/p2p_go_neg.c @@ -0,0 +1,1130 @@ +/* + * Wi-Fi Direct - P2P Group Owner Negotiation + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static int p2p_go_det(u8 own_intent, u8 peer_value) +{ + u8 peer_intent = peer_value >> 1; + if (own_intent == peer_intent) { + if (own_intent == P2P_MAX_GO_INTENT) + return -1; /* both devices want to become GO */ + + /* Use tie breaker bit to determine GO */ + return (peer_value & 0x01) ? 0 : 1; + } + + return own_intent > peer_intent; +} + + +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + const u8 *pos, *end; + struct p2p_channels *ch; + size_t channels; + struct p2p_channels intersection; + + ch = &dev->channels; + os_memset(ch, 0, sizeof(*ch)); + pos = channel_list; + end = channel_list + channel_list_len; + + if (end - pos < 3) + return -1; + os_memcpy(dev->country, pos, 3); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); + if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Mismatching country (ours=%c%c peer's=%c%c)", + p2p->cfg->country[0], p2p->cfg->country[1], + pos[0], pos[1]); + return -1; + } + pos += 3; + + while (pos + 2 < end) { + struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; + cl->reg_class = *pos++; + if (pos + 1 + pos[0] > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: Invalid peer Channel List"); + return -1; + } + channels = *pos++; + cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? + P2P_MAX_REG_CLASS_CHANNELS : channels; + os_memcpy(cl->channel, pos, cl->channels); + pos += channels; + ch->reg_classes++; + if (ch->reg_classes == P2P_MAX_REG_CLASSES) + break; + } + + p2p_channels_intersect(own, &dev->channels, &intersection); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d " + "peer reg_classes %d intersection reg_classes %d", + (int) own->reg_classes, + (int) dev->channels.reg_classes, + (int) intersection.reg_classes); + if (intersection.reg_classes == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, + "P2P: No common channels found"); + return -1; + } + return 0; +} + + +static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + return p2p_peer_channels_check(p2p, &p2p->channels, dev, + channel_list, channel_list_len); +} + + +static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return DEV_PW_REGISTRAR_SPECIFIED; + case WPS_PIN_KEYPAD: + return DEV_PW_USER_SPECIFIED; + case WPS_PBC: + return DEV_PW_PUSHBUTTON; + default: + return DEV_PW_DEFAULT; + } +} + + +static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + default: + return "??"; + } +} + + +static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + peer->dialog_token++; + if (peer->dialog_token == 0) + peer->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + group_capab = 0; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | + p2p->next_tie_breaker); + p2p->next_tie_breaker = !p2p->next_tie_breaker; + p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, p2p->op_channel); + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); + + return buf; +} + + +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct wpabuf *req; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send GO Negotiation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_go_neg_req(p2p, dev); + if (req == NULL) + return -1; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Request"); + p2p_set_state(p2p, P2P_CONNECT); + p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; + p2p->go_neg_peer = dev; + dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->connect_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + u8 tie_breaker) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Building GO Negotiation Response"); + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer && peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); + p2p_buf_add_config_timeout(buf, 100, 20); + if (peer && peer->go_state == REMOTE_GO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " + "Channel attribute"); + } else { + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + } + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + if (status || peer == NULL) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else if (peer->go_state == REMOTE_GO) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else { + struct p2p_channels res; + p2p_channels_intersect(&p2p->channels, &peer->channels, + &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + } + p2p_buf_add_device_info(buf, p2p, peer); + if (peer && peer->go_state == LOCAL_GO) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + p2p_build_wps_ie(p2p, buf, + p2p_wps_method_pw_id(peer ? peer->wps_method : + WPS_NOT_READY), 0); + + return buf; +} + + +static void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection) +{ + struct p2p_reg_class *cl; + int freq; + u8 op_reg_class, op_channel; + unsigned int i; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " + "channel (reg_class %u channel %u) not acceptable to the " + "peer", p2p->op_reg_class, p2p->op_channel); + + /* First, try to pick the best channel from another band */ + freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class, + p2p->op_channel); + if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz " + "channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && + p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz " + "channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + /* Select channel with highest preference if the peer supports it */ + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + if (p2p_channels_includes(intersection, + p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan)) { + p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; + p2p->op_channel = p2p->cfg->pref_chan[i].chan; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick " + "highest preferred chnnel (op_class %u " + "channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + } + + /* + * Fall back to whatever is included in the channel intersection since + * no better options seems to be available. + */ + cl = &intersection->reg_class[0]; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel " + "(reg_class %u channel %u) from intersection", + cl->reg_class, cl->channel[0]); + p2p->op_reg_class = cl->reg_class; + p2p->op_channel = cl->channel[0]; +} + + +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev = NULL; + struct wpabuf *resp; + struct p2p_message msg; + u8 status = P2P_SC_FAIL_INVALID_PARAMS; + int tie_breaker = 0; + int freq; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Request from " MACSTR + "(freq=%d)", MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.capability) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Capability attribute missing from GO " + "Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (msg.go_intent) + tie_breaker = *msg.go_intent & 0x01; + else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory GO Intent attribute missing from GO " + "Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.config_timeout) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Configuration Timeout attribute " + "missing from GO Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.listen_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen Channel attribute received"); + goto fail; + } + if (!msg.operating_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Operating Channel attribute received"); + goto fail; + } + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Channel List attribute received"); + goto fail; + } + if (!msg.intended_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Intended P2P Interface Address attribute " + "received"); + goto fail; + } + if (!msg.p2p_device_info) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No P2P Device Info attribute received"); + goto fail; + } + + if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected GO Negotiation Request SA=" MACSTR + " != dev_addr=" MACSTR, + MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); + goto fail; + } + + dev = p2p_get_device(p2p, sa); + + if (msg.status && *msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Status attribute (%d) in GO " + "Negotiation Request", *msg.status); + goto fail; + } + + if (dev == NULL) + dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); + else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + p2p_add_dev_info(p2p, sa, dev, &msg); + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: User has rejected this peer"); + status = P2P_SC_FAIL_REJECTED_BY_USER; + } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + if (dev) + dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; + p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, + msg.dev_password_id); + } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Already in Group Formation with another peer"); + status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else { + int go; + + if (!p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting " + "GO Negotiation with previously authorized " + "peer"); + if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use default channel settings"); + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use previously configured " + "forced channel settings"); + } + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + + if (!msg.go_intent) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No GO Intent attribute received"); + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + goto fail; + } + + if (dev->go_neg_req_sent && + os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Do not reply since peer has higher " + "address and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Incompatible GO Intent"); + status = P2P_SC_FAIL_BOTH_GO_INTENT_15; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go) { + struct p2p_channels intersection; + size_t i; + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + if (intersection.reg_classes == 0 || + intersection.reg_class[0].channels == 0) { + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + goto fail; + } + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c; + c = &intersection.reg_class[i]; + wpa_printf(MSG_DEBUG, "P2P: reg_class %u", + c->reg_class); + wpa_hexdump(MSG_DEBUG, "P2P: channels", + c->channel, c->channels); + } + if (!p2p_channels_includes(&intersection, + p2p->op_reg_class, + p2p->op_channel)) + p2p_reselect_channel(p2p, &intersection); + + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + } + + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + dev->oper_freq = p2p_channel_to_freq((const char *) + msg.operating_channel, + msg.operating_channel[3], + msg.operating_channel[4]); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " + "channel preference: %d MHz", dev->oper_freq); + + if (msg.config_timeout) { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + if (p2p->state != P2P_IDLE) + p2p_stop_find_for_freq(p2p, rx_freq); + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + dev->dialog_token = msg.dialog_token; + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + p2p->go_neg_peer = dev; + status = P2P_SC_SUCCESS; + } + +fail: + if (dev) + dev->status = status; + resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status, + !tie_breaker); + p2p_parse_free(&msg); + if (resp == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + wpabuf_free(resp); + return; + } + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; + dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; + } else + p2p->pending_action_state = + P2P_PENDING_GO_NEG_RESPONSE_FAILURE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *resp_chan, int go) +{ + struct wpabuf *buf; + u8 *len; + struct p2p_channels res; + u8 group_capab; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Building GO Negotiation Confirm"); + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + if (go || resp_chan == NULL) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel(buf, (const char *) resp_chan, + resp_chan[3], resp_chan[4]); + p2p_channels_intersect(&p2p->channels, &peer->channels, &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + if (go) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct wpabuf *conf; + int go = -1; + struct p2p_message msg; + u8 status = P2P_SC_SUCCESS; + int freq; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Response from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Was not expecting GO Negotiation Response - " + "ignore"); + p2p_parse_free(&msg); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + + if (msg.dialog_token != dev->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation rejected: status %d", + *msg.status); + dev->go_neg_req_sent = 0; + if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Wait for the peer to become ready for " + "GO Negotiation"); + dev->flags |= P2P_DEV_NOT_YET_READY; + dev->wait_count = 0; + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 0); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Stop GO Negotiation attempt"); + p2p_go_neg_failed(p2p, dev, *msg.status); + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_parse_free(&msg); + return; + } + + if (!msg.capability) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Capability attribute missing from GO " + "Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.p2p_device_info) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Device Info attribute missing " + "from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.intended_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Intended P2P Interface Address attribute " + "received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (!msg.go_intent) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No GO Intent attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Incompatible GO Intent"); + status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto fail; + } + + if (!go && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (!go) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Group ID attribute missing from " + "GO Negotiation Response"); + p2p->ssid_len = 0; +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.config_timeout) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Configuration Timeout attribute " + "missing from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } else { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + if (!msg.operating_channel && !go) { + /* + * Note: P2P Client may omit Operating Channel attribute to + * indicate it does not have a preference. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Operating Channel attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Channel List attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (msg.operating_channel) { + dev->oper_freq = p2p_channel_to_freq((const char *) + msg.operating_channel, + msg.operating_channel[3], + msg.operating_channel[4]); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " + "channel preference: %d MHz", dev->oper_freq); + } else + dev->oper_freq = 0; + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: We have wps_method=%s -> " + "incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go) { + struct p2p_channels intersection; + size_t i; + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + if (intersection.reg_classes == 0 || + intersection.reg_class[0].channels == 0) { + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + goto fail; + } + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c; + c = &intersection.reg_class[i]; + wpa_printf(MSG_DEBUG, "P2P: reg_class %u", + c->reg_class); + wpa_hexdump(MSG_DEBUG, "P2P: channels", + c->channel, c->channels); + } + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) + p2p_reselect_channel(p2p, &intersection); + + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + } + + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + +fail: + conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, + msg.operating_channel, go); + p2p_parse_free(&msg); + if (conf == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending GO Negotiation Confirm"); + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + } else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (rx_freq > 0) + freq = rx_freq; + else + freq = dev->listen_freq; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, + wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + p2p_go_neg_failed(p2p, dev, -1); + } + wpabuf_free(conf); +} + + +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GO Negotiation Confirm from " MACSTR, + MAC2STR(sa)); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting " + "for TX status on GO Negotiation Response since we " + "already received Confirmation"); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Was not expecting GO Negotiation Confirm - " + "ignore"); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + if (msg.dialog_token != dev->dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Status attribute received"); + p2p_parse_free(&msg); + return; + } + if (*msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GO Negotiation rejected: status %d", + *msg.status); + p2p_parse_free(&msg); + return; + } + + if (dev->go_state == REMOTE_GO && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (dev->go_state == REMOTE_GO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory P2P Group ID attribute missing from " + "GO Negotiation Confirmation"); + p2p->ssid_len = 0; +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.operating_channel) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Operating Channel attribute missing " + "from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Operating Channel attribute missing " + "from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + p2p_parse_free(&msg); + + if (dev->go_state == UNKNOWN_GO) { + /* + * This should not happen since GO negotiation has already + * been completed. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected GO Neg state - do not know which end " + "becomes GO"); + return; + } + + p2p_go_complete(p2p, dev); +} diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c new file mode 100644 index 0000000..e25baaa --- /dev/null +++ b/src/p2p/p2p_group.c @@ -0,0 +1,739 @@ +/* + * Wi-Fi Direct - P2P group operations + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps_defs.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +struct p2p_group_member { + struct p2p_group_member *next; + u8 addr[ETH_ALEN]; /* P2P Interface Address */ + u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ + struct wpabuf *p2p_ie; + struct wpabuf *client_info; + u8 dev_capab; +}; + +/** + * struct p2p_group - Internal P2P module per-group data + */ +struct p2p_group { + struct p2p_data *p2p; + struct p2p_group_config *cfg; + struct p2p_group_member *members; + unsigned int num_members; + int group_formation; + int beacon_update; + struct wpabuf *noa; +}; + + +static void p2p_group_update_ies(struct p2p_group *group); + + +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config) +{ + struct p2p_group *group, **groups; + + group = os_zalloc(sizeof(*group)); + if (group == NULL) + return NULL; + + groups = os_realloc(p2p->groups, (p2p->num_groups + 1) * + sizeof(struct p2p_group *)); + if (groups == NULL) { + os_free(group); + return NULL; + } + groups[p2p->num_groups++] = group; + p2p->groups = groups; + + group->p2p = p2p; + group->cfg = config; + group->group_formation = 1; + group->beacon_update = 1; + p2p_group_update_ies(group); + group->cfg->idle_update(group->cfg->cb_ctx, 1); + + return group; +} + + +static void p2p_group_free_member(struct p2p_group_member *m) +{ + wpabuf_free(m->p2p_ie); + wpabuf_free(m->client_info); + os_free(m); +} + + +static void p2p_group_free_members(struct p2p_group *group) +{ + struct p2p_group_member *m, *prev; + m = group->members; + group->members = NULL; + group->num_members = 0; + while (m) { + prev = m; + m = m->next; + p2p_group_free_member(prev); + } +} + + +void p2p_group_deinit(struct p2p_group *group) +{ + size_t g; + struct p2p_data *p2p; + + if (group == NULL) + return; + + p2p = group->p2p; + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p->groups[g] == group) { + while (g + 1 < p2p->num_groups) { + p2p->groups[g] = p2p->groups[g + 1]; + g++; + } + p2p->num_groups--; + break; + } + } + + p2p_group_free_members(group); + os_free(group->cfg); + wpabuf_free(group->noa); + os_free(group); +} + + +static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m) +{ + if (m->client_info == NULL) + return; + if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1) + return; + wpabuf_put_buf(ie, m->client_info); +} + + +static void p2p_group_add_common_ies(struct p2p_group *group, + struct wpabuf *ie) +{ + u8 dev_capab = 0, group_capab = 0; + + /* P2P Capability */ + dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if (group->cfg->persistent_group) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (group->cfg->persistent_group == 2) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (group->p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + if (group->group_formation) + group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION; + if (group->p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (group->num_members >= group->cfg->max_clients) + group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + p2p_buf_add_capability(ie, dev_capab, group_capab); +} + + +static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) +{ + if (noa == NULL) + return; + /* Notice of Absence */ + wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(ie, wpabuf_len(noa)); + wpabuf_put_buf(ie, noa); +} + + +static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) +{ + struct wpabuf *ie; + u8 *len; + + ie = wpabuf_alloc(257); + if (ie == NULL) + return NULL; + + len = p2p_buf_add_ie_hdr(ie); + p2p_group_add_common_ies(group, ie); + p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); + p2p_group_add_noa(ie, group->noa); + p2p_buf_update_ie_hdr(ie, len); + + return ie; +} + + +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + u8 *group_info; + struct wpabuf *ie; + struct p2p_group_member *m; + u8 *len; + + ie = wpabuf_alloc(257); + if (ie == NULL) + return NULL; + + len = p2p_buf_add_ie_hdr(ie); + + p2p_group_add_common_ies(group, ie); + p2p_group_add_noa(ie, group->noa); + + /* P2P Device Info */ + p2p_buf_add_device_info(ie, group->p2p, NULL); + + /* P2P Group Info */ + group_info = wpabuf_put(ie, 0); + wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(ie, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) + p2p_client_info(ie, m); + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(ie, 0) - group_info - 3); + + p2p_buf_update_ie_hdr(ie, len); + return ie; +} + + +static void p2p_group_update_ies(struct p2p_group *group) +{ + struct wpabuf *beacon_ie; + struct wpabuf *probe_resp_ie; + + probe_resp_ie = p2p_group_build_probe_resp_ie(group); + if (probe_resp_ie == NULL) + return; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE", + probe_resp_ie); + + if (group->beacon_update) { + beacon_ie = p2p_group_build_beacon_ie(group); + if (beacon_ie) + group->beacon_update = 0; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE", + beacon_ie); + } else + beacon_ie = NULL; + + group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie); +} + + +/** + * p2p_build_client_info - Build P2P Client Info Descriptor + * @addr: MAC address of the peer device + * @p2p_ie: P2P IE from (Re)Association Request + * @dev_capab: Buffer for returning Device Capability + * @dev_addr: Buffer for returning P2P Device Address + * Returns: P2P Client Info Descriptor or %NULL on failure + * + * This function builds P2P Client Info Descriptor based on the information + * available from (Re)Association Request frame. Group owner can use this to + * build the P2P Group Info attribute for Probe Response frames. + */ +static struct wpabuf * p2p_build_client_info(const u8 *addr, + struct wpabuf *p2p_ie, + u8 *dev_capab, u8 *dev_addr) +{ + const u8 *spos; + struct p2p_message msg; + u8 *len_pos; + struct wpabuf *buf; + + if (p2p_ie == NULL) + return NULL; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) || + msg.capability == NULL || msg.p2p_device_info == NULL) + return NULL; + + buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len); + if (buf == NULL) + return NULL; + + *dev_capab = msg.capability[0]; + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + + spos = msg.p2p_device_info; /* P2P Device address */ + + /* P2P Client Info Descriptor */ + /* Length to be set */ + len_pos = wpabuf_put(buf, 1); + /* P2P Device address */ + wpabuf_put_data(buf, spos, ETH_ALEN); + /* P2P Interface address */ + wpabuf_put_data(buf, addr, ETH_ALEN); + /* Device Capability Bitmap */ + wpabuf_put_u8(buf, msg.capability[0]); + /* + * Config Methods, Primary Device Type, Number of Secondary Device + * Types, Secondary Device Type List, Device Name copied from + * Device Info + */ + wpabuf_put_data(buf, spos + ETH_ALEN, + msg.p2p_device_info_len - ETH_ALEN); + + *len_pos = wpabuf_len(buf) - 1; + + + return buf; +} + + +static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m, *prev; + + if (group == NULL) + return 0; + + m = group->members; + prev = NULL; + while (m) { + if (os_memcmp(m->addr, addr, ETH_ALEN) == 0) + break; + prev = m; + m = m->next; + } + + if (m == NULL) + return 0; + + if (prev) + prev->next = m->next; + else + group->members = m->next; + p2p_group_free_member(m); + group->num_members--; + + return 1; +} + + +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len) +{ + struct p2p_group_member *m; + + if (group == NULL) + return -1; + + m = os_zalloc(sizeof(*m)); + if (m == NULL) + return -1; + os_memcpy(m->addr, addr, ETH_ALEN); + m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE); + if (m->p2p_ie) { + m->client_info = p2p_build_client_info(addr, m->p2p_ie, + &m->dev_capab, + m->dev_addr); + } + + p2p_group_remove_member(group, addr); + + m->next = group->members; + group->members = m; + group->num_members++; + wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR + " to group (p2p=%d client_info=%d); num_members=%u/%u", + MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0, + group->num_members, group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 1) + group->cfg->idle_update(group->cfg->cb_ctx, 0); + + return 0; +} + + +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) +{ + struct wpabuf *resp; + u8 *rlen; + + /* + * (Re)Association Response - P2P IE + * Status attribute (shall be present when association request is + * denied) + * Extended Listen Timing (may be present) + */ + resp = wpabuf_alloc(20); + if (resp == NULL) + return NULL; + rlen = p2p_buf_add_ie_hdr(resp); + if (status != P2P_SC_SUCCESS) + p2p_buf_add_status(resp, status); + p2p_buf_update_ie_hdr(resp, rlen); + + return resp; +} + + +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) +{ + if (p2p_group_remove_member(group, addr)) { + wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove " + "client " MACSTR " from group; num_members=%u/%u", + MAC2STR(addr), group->num_members, + group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients - 1) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 0) + group->cfg->idle_update(group->cfg->cb_ctx, 1); + } +} + + +/** + * p2p_match_dev_type_member - Match client device type with requested type + * @m: Group member + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. + */ +static int p2p_match_dev_type_member(struct p2p_group_member *m, + struct wpabuf *wps) +{ + const u8 *pos, *end; + struct wps_parse_attr attr; + u8 num_sec; + + if (m->client_info == NULL || wps == NULL) + return 0; + + pos = wpabuf_head(m->client_info); + end = pos + wpabuf_len(m->client_info); + + pos += 1 + 2 * ETH_ALEN + 1 + 2; + if (end - pos < WPS_DEV_TYPE_LEN + 1) + return 0; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type)) + return 1; /* Match with client Primary Device Type */ + + pos += WPS_DEV_TYPE_LEN; + num_sec = *pos++; + if (end - pos < num_sec * WPS_DEV_TYPE_LEN) + return 0; + while (num_sec > 0) { + num_sec--; + if (dev_type_list_match(pos, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Match with client Secondary Device Type */ + pos += WPS_DEV_TYPE_LEN; + } + + /* No matching device type found */ + return 0; +} + + +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps) +{ + struct p2p_group_member *m; + + if (p2p_match_dev_type(group->p2p, wps)) + return 1; /* Match with own device type */ + + for (m = group->members; m; m = m->next) { + if (p2p_match_dev_type_member(m, wps)) + return 1; /* Match with group client device type */ + } + + /* No match with Requested Device Type */ + return 0; +} + + +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p) +{ + struct p2p_group_member *m; + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p, &msg)) + return 1; /* Failed to parse - assume no filter on Device ID */ + + if (!msg.device_id) + return 1; /* No filter on Device ID */ + + if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with our P2P Device Address */ + + for (m = group->members; m; m = m->next) { + if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with group client P2P Device Address */ + } + + /* No match with Device ID */ + return 0; +} + + +void p2p_group_notif_formation_done(struct p2p_group *group) +{ + if (group == NULL) + return; + group->group_formation = 0; + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len) +{ + if (noa == NULL) { + wpabuf_free(group->noa); + group->noa = NULL; + } else { + if (group->noa) { + if (wpabuf_size(group->noa) >= noa_len) { + group->noa->used = 0; + wpabuf_put_data(group->noa, noa, noa_len); + } else { + wpabuf_free(group->noa); + group->noa = NULL; + } + } + + if (!group->noa) { + group->noa = wpabuf_alloc_copy(noa, noa_len); + if (group->noa == NULL) + return -1; + } + } + + group->beacon_update = 1; + p2p_group_update_ies(group); + return 0; +} + + +static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, + const u8 *dev_id) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +static struct p2p_group_member * p2p_group_get_client_iface( + struct p2p_group *group, const u8 *interface_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m; + + if (group == NULL) + return NULL; + m = p2p_group_get_client_iface(group, addr); + if (m && !is_zero_ether_addr(m->dev_addr)) + return m->dev_addr; + return NULL; +} + + +static struct wpabuf * p2p_build_go_disc_req(void) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0); + + return buf; +} + + +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq) +{ + struct p2p_group_member *m; + struct wpabuf *req; + struct p2p_data *p2p = group->p2p; + int freq; + + m = p2p_group_get_client(group, dev_id); + if (m == NULL || m->client_info == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this " + "group " MACSTR, + MAC2STR(group->cfg->interface_addr)); + return -1; + } + + if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_printf(MSG_DEBUG, "P2P: Requested client does not support " + "client discoverability"); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be " + "sent to " MACSTR, MAC2STR(dev_id)); + + req = p2p_build_go_disc_req(); + if (req == NULL) + return -1; + + /* TODO: Should really use group operating frequency here */ + freq = rx_freq; + + p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ; + if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, + group->cfg->interface_addr, + group->cfg->interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(req); + + return 0; +} + + +const u8 * p2p_group_get_interface_addr(struct p2p_group *group) +{ + return group->cfg->interface_addr; +} + + +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len) +{ + struct p2p_group_member *m; + u8 curr_noa[50]; + int curr_noa_len; + + m = p2p_group_get_client_iface(group, client_interface_addr); + if (m == NULL || m->client_info == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Client was not in this group"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + + wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len); + + if (group->p2p->cfg->get_noa) + curr_noa_len = group->p2p->cfg->get_noa( + group->p2p->cfg->cb_ctx, group->cfg->interface_addr, + curr_noa, sizeof(curr_noa)); + else + curr_noa_len = -1; + if (curr_noa_len < 0) + wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA"); + else if (curr_noa_len == 0) + wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized"); + else + wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, + curr_noa_len); + + /* TODO: properly process request and store copy */ + if (curr_noa_len > 0 || curr_noa_len == -1) + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + + return P2P_SC_SUCCESS; +} + + +unsigned int p2p_get_group_num_members(struct p2p_group *group) +{ + return group->num_members; +} + + +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) +{ + struct p2p_group_member *iter = *next; + + if (!iter) + iter = group->members; + else + iter = iter->next; + + *next = iter; + + if (!iter) + return NULL; + + return iter->addr; +} + + +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0) + return 1; + } + + return 0; +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h new file mode 100644 index 0000000..d052a03 --- /dev/null +++ b/src/p2p/p2p_i.h @@ -0,0 +1,678 @@ +/* + * P2P - Internal definitions for P2P module + * Copyright (c) 2009-2010, 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. + */ + +#ifndef P2P_I_H +#define P2P_I_H + +#include "utils/list.h" +#include "p2p.h" + +enum p2p_go_state { + UNKNOWN_GO, + LOCAL_GO, + REMOTE_GO +}; + +/** + * struct p2p_device - P2P Device data (internal to P2P module) + */ +struct p2p_device { + struct dl_list list; + struct os_time last_seen; + int listen_freq; + enum p2p_wps_method wps_method; + + struct p2p_peer_info info; + + /* + * If the peer was discovered based on an interface address (e.g., GO + * from Beacon/Probe Response), the interface address is stored here. + * p2p_device_addr must still be set in such a case to the unique + * identifier for the P2P Device. + */ + u8 interface_addr[ETH_ALEN]; + + /* + * P2P Device Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_dev[ETH_ALEN]; + + /* + * P2P Interface Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_iface[ETH_ALEN]; + + int go_neg_req_sent; + enum p2p_go_state go_state; + u8 dialog_token; + u8 intended_addr[ETH_ALEN]; + + char country[3]; + struct p2p_channels channels; + int oper_freq; + u8 oper_ssid[32]; + size_t oper_ssid_len; + + /** + * req_config_methods - Pending provisioning discovery methods + */ + u16 req_config_methods; + + /** + * wps_prov_info - Stored provisioning WPS config method + * + * This is used to store pending WPS config method between Provisioning + * Discovery and connection to a running group. + */ + u16 wps_prov_info; + +#define P2P_DEV_PROBE_REQ_ONLY BIT(0) +#define P2P_DEV_REPORTED BIT(1) +#define P2P_DEV_NOT_YET_READY BIT(2) +#define P2P_DEV_SD_INFO BIT(3) +#define P2P_DEV_SD_SCHEDULE BIT(4) +#define P2P_DEV_PD_PEER_DISPLAY BIT(5) +#define P2P_DEV_PD_PEER_KEYPAD BIT(6) +#define P2P_DEV_USER_REJECTED BIT(7) +#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8) +#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9) +#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10) +#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11) +#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12) +#define P2P_DEV_FORCE_FREQ BIT(13) +#define P2P_DEV_PD_FOR_JOIN BIT(14) +#define P2P_DEV_REPORTED_ONCE BIT(15) +#define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) + unsigned int flags; + + int status; /* enum p2p_status_code */ + unsigned int wait_count; + unsigned int connect_reqs; + unsigned int invitation_reqs; + + u16 ext_listen_period; + u16 ext_listen_interval; + + u8 go_timeout; + u8 client_timeout; +}; + +struct p2p_sd_query { + struct p2p_sd_query *next; + u8 peer[ETH_ALEN]; + int for_all_peers; + struct wpabuf *tlvs; +}; + +struct p2p_pending_action_tx { + unsigned int freq; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + size_t len; + unsigned int wait_time; + /* Followed by len octets of the frame */ +}; + +/** + * struct p2p_data - P2P module data (internal to P2P module) + */ +struct p2p_data { + /** + * cfg - P2P module configuration + * + * This is included in the same memory allocation with the + * struct p2p_data and as such, must not be freed separately. + */ + struct p2p_config *cfg; + + /** + * state - The current P2P state + */ + enum p2p_state { + /** + * P2P_IDLE - Idle + */ + P2P_IDLE, + + /** + * P2P_SEARCH - Search (Device Discovery) + */ + P2P_SEARCH, + + /** + * P2P_CONNECT - Trying to start GO Negotiation + */ + P2P_CONNECT, + + /** + * P2P_CONNECT_LISTEN - Listen during GO Negotiation start + */ + P2P_CONNECT_LISTEN, + + /** + * P2P_GO_NEG - In GO Negotiation + */ + P2P_GO_NEG, + + /** + * P2P_LISTEN_ONLY - Listen only + */ + P2P_LISTEN_ONLY, + + /** + * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg + */ + P2P_WAIT_PEER_CONNECT, + + /** + * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg + */ + P2P_WAIT_PEER_IDLE, + + /** + * P2P_SD_DURING_FIND - Service Discovery during find + */ + P2P_SD_DURING_FIND, + + /** + * P2P_PROVISIONING - Provisioning (during group formation) + */ + P2P_PROVISIONING, + + /** + * P2P_PD_DURING_FIND - Provision Discovery during find + */ + P2P_PD_DURING_FIND, + + /** + * P2P_INVITE - Trying to start Invite + */ + P2P_INVITE, + + /** + * P2P_INVITE_LISTEN - Listen during Invite + */ + P2P_INVITE_LISTEN, + + /** + * P2P_SEARCH_WHEN_READY - Waiting to start Search + */ + P2P_SEARCH_WHEN_READY, + } state; + + /** + * min_disc_int - minDiscoverableInterval + */ + int min_disc_int; + + /** + * max_disc_int - maxDiscoverableInterval + */ + int max_disc_int; + + /** + * devices - List of known P2P Device peers + */ + struct dl_list devices; + + /** + * go_neg_peer - Pointer to GO Negotiation peer + */ + struct p2p_device *go_neg_peer; + + /** + * invite_peer - Pointer to Invite peer + */ + struct p2p_device *invite_peer; + + const u8 *invite_go_dev_addr; + u8 invite_go_dev_addr_buf[ETH_ALEN]; + + /** + * sd_peer - Pointer to Service Discovery peer + */ + struct p2p_device *sd_peer; + + /** + * sd_query - Pointer to Service Discovery query + */ + struct p2p_sd_query *sd_query; + + /* GO Negotiation data */ + + /** + * intended_addr - Local Intended P2P Interface Address + * + * This address is used during group owner negotiation as the Intended + * P2P Interface Address and the group interface will be created with + * address as the local address in case of successfully completed + * negotiation. + */ + u8 intended_addr[ETH_ALEN]; + + /** + * go_intent - Local GO Intent to be used during GO Negotiation + */ + u8 go_intent; + + /** + * next_tie_breaker - Next tie-breaker value to use in GO Negotiation + */ + u8 next_tie_breaker; + + /** + * ssid - Selected SSID for GO Negotiation (if local end will be GO) + */ + u8 ssid[32]; + + /** + * ssid_len - ssid length in octets + */ + size_t ssid_len; + + /** + * ssid_set - Whether SSID is already set for GO Negotiation + */ + int ssid_set; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + enum p2p_pending_action_state { + P2P_NO_PENDING_ACTION, + P2P_PENDING_GO_NEG_REQUEST, + P2P_PENDING_GO_NEG_RESPONSE, + P2P_PENDING_GO_NEG_RESPONSE_FAILURE, + P2P_PENDING_GO_NEG_CONFIRM, + P2P_PENDING_SD, + P2P_PENDING_PD, + P2P_PENDING_INVITATION_REQUEST, + P2P_PENDING_INVITATION_RESPONSE, + P2P_PENDING_DEV_DISC_REQUEST, + P2P_PENDING_DEV_DISC_RESPONSE, + P2P_PENDING_GO_DISC_REQ + } pending_action_state; + + unsigned int pending_listen_freq; + unsigned int pending_listen_sec; + unsigned int pending_listen_usec; + + u8 dev_capab; + + int in_listen; + int drv_in_listen; + + /** + * sd_queries - Pending service discovery queries + */ + struct p2p_sd_query *sd_queries; + + /** + * srv_update_indic - Service Update Indicator for local services + */ + u16 srv_update_indic; + + struct wpabuf *sd_resp; /* Fragmented SD response */ + u8 sd_resp_addr[ETH_ALEN]; + u8 sd_resp_dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + + struct wpabuf *sd_rx_resp; /* Reassembled SD response */ + u16 sd_rx_update_indic; + + /* P2P Invitation data */ + enum p2p_invite_role inv_role; + u8 inv_bssid[ETH_ALEN]; + int inv_bssid_set; + u8 inv_ssid[32]; + size_t inv_ssid_len; + u8 inv_sa[ETH_ALEN]; + u8 inv_group_bssid[ETH_ALEN]; + u8 *inv_group_bssid_ptr; + u8 inv_go_dev_addr[ETH_ALEN]; + u8 inv_status; + int inv_op_freq; + int inv_persistent; + + enum p2p_discovery_type find_type; + unsigned int last_p2p_find_timeout; + u8 last_prog_scan_class; + u8 last_prog_scan_chan; + int p2p_scan_running; + enum p2p_after_scan { + P2P_AFTER_SCAN_NOTHING, + P2P_AFTER_SCAN_LISTEN, + P2P_AFTER_SCAN_CONNECT + } start_after_scan; + u8 after_scan_peer[ETH_ALEN]; + struct p2p_pending_action_tx *after_scan_tx; + + /* Requested device types for find/search */ + unsigned int num_req_dev_types; + u8 *req_dev_types; + u8 *find_dev_id; + u8 find_dev_id_buf[ETH_ALEN]; + + struct p2p_group **groups; + size_t num_groups; + + struct p2p_device *pending_client_disc_go; + u8 pending_client_disc_addr[ETH_ALEN]; + u8 pending_dev_disc_dialog_token; + u8 pending_dev_disc_addr[ETH_ALEN]; + int pending_dev_disc_freq; + unsigned int pending_client_disc_freq; + + int ext_listen_only; + unsigned int ext_listen_period; + unsigned int ext_listen_interval; + unsigned int ext_listen_interval_sec; + unsigned int ext_listen_interval_usec; + + u8 peer_filter[ETH_ALEN]; + + int cross_connect; + + int best_freq_24; + int best_freq_5; + int best_freq_overall; + + /** + * wps_vendor_ext - WPS Vendor Extensions to add + */ + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /* + * user_initiated_pd - Whether a PD request is user initiated or not. + */ + u8 user_initiated_pd; + + /* + * Keep track of which peer a given PD request was sent to. + * Used to raise a timeout alert in case there is no response. + */ + u8 pending_pd_devaddr[ETH_ALEN]; + + /* + * Retry counter for provision discovery requests when issued + * in IDLE state. + */ + int pd_retries; +}; + +/** + * struct p2p_message - Parsed P2P message (or P2P IE) + */ +struct p2p_message { + struct wpabuf *p2p_attributes; + struct wpabuf *wps_attributes; + + u8 dialog_token; + + const u8 *capability; + const u8 *go_intent; + const u8 *status; + const u8 *listen_channel; + const u8 *operating_channel; + const u8 *channel_list; + u8 channel_list_len; + const u8 *config_timeout; + const u8 *intended_addr; + const u8 *group_bssid; + const u8 *invitation_flags; + + const u8 *group_info; + size_t group_info_len; + + const u8 *group_id; + size_t group_id_len; + + const u8 *device_id; + + const u8 *manageability; + + const u8 *noa; + size_t noa_len; + + const u8 *ext_listen_timing; + + const u8 *minor_reason_code; + + /* P2P Device Info */ + const u8 *p2p_device_info; + size_t p2p_device_info_len; + const u8 *p2p_device_addr; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + char device_name[33]; + u16 config_methods; + + /* WPS IE */ + u16 dev_password_id; + u16 wps_config_methods; + const u8 *wps_pri_dev_type; + const u8 *wps_sec_dev_type_list; + size_t wps_sec_dev_type_list_len; + const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT]; + 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; + + /* DS Parameter Set IE */ + const u8 *ds_params; + + /* SSID IE */ + const u8 *ssid; +}; + + +#define P2P_MAX_GROUP_ENTRIES 50 + +struct p2p_group_info { + unsigned int num_clients; + struct p2p_client_info { + const u8 *p2p_device_addr; + const u8 *p2p_interface_addr; + u8 dev_capab; + u16 config_methods; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + const u8 *sec_dev_types; + const char *dev_name; + size_t dev_name_len; + } client[P2P_MAX_GROUP_ENTRIES]; +}; + + +/* p2p_utils.c */ +int p2p_random(char *buf, size_t len); +int p2p_channel_to_freq(const char *country, int reg_class, int channel); +int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, + u8 *channel); +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel); + +/* p2p_parse.c */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +void p2p_parse_free(struct p2p_message *msg); +int p2p_attr_text(struct wpabuf *data, char *buf, char *end); +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info); + +/* p2p_build.c */ + +struct p2p_noa_desc { + u8 count_type; + u32 duration; + u32 interval; + u32 start_time; +}; + +/* p2p_group.c */ +const u8 * p2p_group_get_interface_addr(struct p2p_group *group); +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len); + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token); +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); +void p2p_buf_add_status(struct wpabuf *buf, u8 status); +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer); +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr); +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len); +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab); +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent); +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan); +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout); +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr); +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid); +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len); +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags); +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2); +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval); +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, + int all_attr); + +/* p2p_sd.c */ +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev); +void p2p_free_sd_queries(struct p2p_data *p2p); +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); + +/* p2p_go_neg.c */ +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len); +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); + +/* p2p_pd.c */ +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq); +void p2p_reset_pending_pd(struct p2p_data *p2p); + +/* p2p_invitation.c */ +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr); +void p2p_invitation_req_cb(struct p2p_data *p2p, int success); +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); + +/* p2p_dev_disc.c */ +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success); +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev); +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success); +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success); +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq); + +/* p2p.c */ +void p2p_set_state(struct p2p_data *p2p, int new_state); +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, + unsigned int usec); +void p2p_clear_timeout(struct p2p_data *p2p); +void p2p_continue_find(struct p2p_data *p2p); +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg); +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg); +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, + const u8 *ies, size_t ies_len); +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, + const u8 *addr); +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status); +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); + +#endif /* P2P_I_H */ diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c new file mode 100644 index 0000000..016c572 --- /dev/null +++ b/src/p2p/p2p_invitation.c @@ -0,0 +1,491 @@ +/* + * Wi-Fi Direct - P2P Invitation procedure + * Copyright (c) 2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, + struct p2p_device *peer, + const u8 *go_dev_addr) +{ + struct wpabuf *buf; + u8 *len; + const u8 *dev_addr; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + peer->dialog_token++; + if (peer->dialog_token == 0) + peer->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, + peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) + p2p_buf_add_config_timeout(buf, 0, 0); + else + p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? + P2P_INVITATION_FLAGS_TYPE : 0); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, p2p->op_channel); + if (p2p->inv_bssid_set) + p2p_buf_add_group_bssid(buf, p2p->inv_bssid); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + if (go_dev_addr) + dev_addr = go_dev_addr; + else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) + dev_addr = peer->info.p2p_device_addr; + else + dev_addr = p2p->cfg->dev_addr; + p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *group_bssid, + u8 reg_class, u8 channel, + struct p2p_channels *channels) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, + dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ + if (reg_class && channel) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + reg_class, channel); + if (group_bssid) + p2p_buf_add_group_bssid(buf, group_bssid); + if (channels) + p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct p2p_message msg; + struct wpabuf *resp = NULL; + u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + int freq; + int go = 0; + u8 group_bssid[ETH_ALEN], *bssid; + int op_freq = 0; + u8 reg_class = 0, channel = 0; + struct p2p_channels intersection, *channels = NULL; + int persistent; + + os_memset(group_bssid, 0, sizeof(group_bssid)); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Invitation Request from " MACSTR " (freq=%d)", + MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request add device failed " + MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Reject Invitation Request from unknown " + "peer " MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + } + + if (!msg.group_id || !msg.channel_list) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory attribute missing in Invitation " + "Request from " MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (msg.invitation_flags) + persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE; + else { + /* Invitation Flags is a mandatory attribute starting from P2P + * spec 1.06. As a backwards compatibility mechanism, assume + * the request was for a persistent group if the attribute is + * missing. + */ + wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags " + "attribute missing from Invitation Request"); + persistent = 1; + } + + if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, + msg.channel_list, msg.channel_list_len) < + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (p2p->cfg->invitation_process) { + status = p2p->cfg->invitation_process( + p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, + msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, + &go, group_bssid, &op_freq, persistent); + } + + if (op_freq) { + if (p2p_freq_to_channel(p2p->cfg->country, op_freq, + ®_class, &channel) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown forced freq %d MHz from " + "invitation_process()", op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + if (!p2p_channels_includes(&intersection, reg_class, channel)) + { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: forced freq %d MHz not in the supported " + "channels interaction", op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (status == P2P_SC_SUCCESS) + channels = &intersection; + } else { + op_freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel); + if (op_freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown operational channel " + "(country=%c%c reg_class=%u channel=%u)", + p2p->cfg->country[0], p2p->cfg->country[1], + p2p->cfg->op_reg_class, p2p->cfg->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + if (status == P2P_SC_SUCCESS) { + reg_class = p2p->cfg->op_reg_class; + channel = p2p->cfg->op_channel; + channels = &intersection; + } + } + +fail: + if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid)) + bssid = group_bssid; + else + bssid = NULL; + resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, + bssid, reg_class, channel, channels); + + if (resp == NULL) + goto out; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + goto out; + } + + /* + * Store copy of invitation data to be used when processing TX status + * callback for the Acton frame. + */ + os_memcpy(p2p->inv_sa, sa, ETH_ALEN); + if (msg.group_bssid) { + os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN); + p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; + } else + p2p->inv_group_bssid_ptr = NULL; + if (msg.group_id_len - ETH_ALEN <= 32) { + os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, + msg.group_id_len - ETH_ALEN); + p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + } + os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); + p2p->inv_status = status; + p2p->inv_op_freq = op_freq; + + p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + +out: + wpabuf_free(resp); + p2p_parse_free(&msg); +} + + +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Invitation Response from " MACSTR, + MAC2STR(sa)); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Invitation Response from unknown peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (dev != p2p->invite_peer) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected Invitation Response from peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.status) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Mandatory Status attribute missing in " + "Invitation Response from " MACSTR, MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, + msg.group_bssid); + + p2p_parse_free(&msg); + + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + p2p->invite_peer = NULL; +} + + +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr) +{ + struct wpabuf *req; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send Invitation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + if (req == NULL) + return -1; + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Invitation Request"); + p2p_set_state(p2p, P2P_INVITE); + p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; + p2p->invite_peer = dev; + dev->invitation_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } + + wpabuf_free(req); + + return 0; +} + + +void p2p_invitation_req_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Request TX callback: success=%d", success); + + if (p2p->invite_peer == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No pending Invite"); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_INVITE); + p2p_set_timeout(p2p, 0, 100000); +} + + +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation Response TX callback: success=%d", success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (success && p2p->cfg->invitation_received) { + p2p->cfg->invitation_received(p2p->cfg->cb_ctx, + p2p->inv_sa, + p2p->inv_group_bssid_ptr, + p2p->inv_ssid, p2p->inv_ssid_len, + p2p->inv_go_dev_addr, + p2p->inv_status, + p2p->inv_op_freq); + } +} + + +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group) +{ + struct p2p_device *dev; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Request to invite peer " MACSTR " role=%d persistent=%d " + "force_freq=%u", + MAC2STR(peer), role, persistent_group, force_freq); + if (bssid) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid)); + if (go_dev_addr) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invitation for GO Device Address " MACSTR, + MAC2STR(go_dev_addr)); + os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); + p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; + } else + p2p->invite_go_dev_addr = NULL; + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID", + ssid, ssid_len); + + dev = p2p_get_device(p2p, peer); + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot invite unknown P2P Device " MACSTR, + MAC2STR(peer)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot invite a P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer)); + } + /* TODO: use device discoverability request through GO */ + } + + dev->invitation_reqs = 0; + + if (force_freq) { + if (p2p_freq_to_channel(p2p->cfg->country, force_freq, + &p2p->op_reg_class, &p2p->op_channel) < + 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported frequency %u MHz", + force_freq); + return -1; + } + p2p->channels.reg_classes = 1; + p2p->channels.reg_class[0].channels = 1; + p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; + p2p->channels.reg_class[0].channel[0] = p2p->op_channel; + } else { + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + p2p->inv_role = role; + p2p->inv_bssid_set = bssid != NULL; + if (bssid) + os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN); + os_memcpy(p2p->inv_ssid, ssid, ssid_len); + p2p->inv_ssid_len = ssid_len; + p2p->inv_persistent = persistent_group; + return p2p_invite_send(p2p, dev, go_dev_addr); +} diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c new file mode 100644 index 0000000..5c5445a --- /dev/null +++ b/src/p2p/p2p_parse.c @@ -0,0 +1,718 @@ +/* + * P2P - IE parser + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps_i.h" +#include "p2p_i.h" + + +static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, + struct p2p_message *msg) +{ + const u8 *pos; + size_t i, nlen; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + switch (id) { + case P2P_ATTR_CAPABILITY: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Capability " + "attribute (length %d)", len); + return -1; + } + msg->capability = data; + wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x " + "Group Capability %02x", + data[0], data[1]); + break; + case P2P_ATTR_DEVICE_ID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device ID " + "attribute (length %d)", len); + return -1; + } + msg->device_id = data; + wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR, + MAC2STR(msg->device_id)); + break; + case P2P_ATTR_GROUP_OWNER_INTENT: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent " + "attribute (length %d)", len); + return -1; + } + msg->go_intent = data; + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u " + "Tie breaker %u", data[0] >> 1, data[0] & 0x01); + break; + case P2P_ATTR_STATUS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Status " + "attribute (length %d)", len); + return -1; + } + msg->status = data; + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]); + break; + case P2P_ATTR_LISTEN_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore " + "null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel " + "attribute (length %d)", len); + return -1; + } + msg->listen_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_OPERATING_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Ignore null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Operating " + "Channel attribute (length %d)", len); + return -1; + } + msg->operating_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_CHANNEL_LIST: + if (len < 3) { + wpa_printf(MSG_DEBUG, "P2P: Too short Channel List " + "attribute (length %d)", len); + return -1; + } + msg->channel_list = data; + msg->channel_list_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String " + "'%c%c(0x%02x)'", data[0], data[1], data[2]); + wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List", + msg->channel_list, msg->channel_list_len); + break; + case P2P_ATTR_GROUP_INFO: + msg->group_info = data; + msg->group_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Group Info"); + break; + case P2P_ATTR_DEVICE_INFO: + if (len < ETH_ALEN + 2 + 8 + 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device Info " + "attribute (length %d)", len); + return -1; + } + msg->p2p_device_info = data; + msg->p2p_device_info_len = len; + pos = data; + msg->p2p_device_addr = pos; + pos += ETH_ALEN; + msg->config_methods = WPA_GET_BE16(pos); + pos += 2; + msg->pri_dev_type = pos; + pos += 8; + msg->num_sec_dev_types = *pos++; + if (msg->num_sec_dev_types * 8 > data + len - pos) { + wpa_printf(MSG_DEBUG, "P2P: Device Info underflow"); + return -1; + } + pos += msg->num_sec_dev_types * 8; + if (data + len - pos < 4) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d", (int) (data + len - pos)); + return -1; + } + if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) { + wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name " + "header", pos, 4); + return -1; + } + pos += 2; + nlen = WPA_GET_BE16(pos); + pos += 2; + if (data + len - pos < (int) nlen || nlen > 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d (buf len %d)", (int) nlen, + (int) (data + len - pos)); + return -1; + } + os_memcpy(msg->device_name, pos, nlen); + msg->device_name[nlen] = '\0'; + for (i = 0; i < nlen; i++) { + if (msg->device_name[i] == '\0') + break; + if (msg->device_name[i] > 0 && + msg->device_name[i] < 32) + msg->device_name[i] = '_'; + } + wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR + " primary device type %s device name '%s' " + "config methods 0x%x", + MAC2STR(msg->p2p_device_addr), + wps_dev_type_bin2str(msg->pri_dev_type, devtype, + sizeof(devtype)), + msg->device_name, msg->config_methods); + break; + case P2P_ATTR_CONFIGURATION_TIMEOUT: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Configuration " + "Timeout attribute (length %d)", len); + return -1; + } + msg->config_timeout = data; + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout"); + break; + case P2P_ATTR_INTENDED_INTERFACE_ADDR: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P " + "Interface Address attribute (length %d)", + len); + return -1; + } + msg->intended_addr = data; + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: " + MACSTR, MAC2STR(msg->intended_addr)); + break; + case P2P_ATTR_GROUP_BSSID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID " + "attribute (length %d)", len); + return -1; + } + msg->group_bssid = data; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR, + MAC2STR(msg->group_bssid)); + break; + case P2P_ATTR_GROUP_ID: + if (len < ETH_ALEN || len > ETH_ALEN + 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " + "attribute length %d", len); + return -1; + } + msg->group_id = data; + msg->group_id_len = len; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address " + MACSTR, MAC2STR(msg->group_id)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID", + msg->group_id + ETH_ALEN, + msg->group_id_len - ETH_ALEN); + break; + case P2P_ATTR_INVITATION_FLAGS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Invitation " + "Flag attribute (length %d)", len); + return -1; + } + msg->invitation_flags = data; + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_MANAGEABILITY: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Manageability " + "attribute (length %d)", len); + return -1; + } + msg->manageability = data; + wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_NOTICE_OF_ABSENCE: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Notice of " + "Absence attribute (length %d)", len); + return -1; + } + msg->noa = data; + msg->noa_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); + break; + case P2P_ATTR_EXT_LISTEN_TIMING: + if (len < 4) { + wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen " + "Timing attribute (length %d)", len); + return -1; + } + msg->ext_listen_timing = data; + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing " + "(period %u msec interval %u msec)", + WPA_GET_LE16(msg->ext_listen_timing), + WPA_GET_LE16(msg->ext_listen_timing + 2)); + break; + case P2P_ATTR_MINOR_REASON_CODE: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason " + "Code attribute (length %d)", len); + return -1; + } + msg->minor_reason_code = data; + wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", + *msg->minor_reason_code); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " + "(length %d)", id, len); + break; + } + + return 0; +} + + +/** + * p2p_parse_p2p_ie - Parse P2P IE + * @buf: Concatenated P2P IE(s) payload + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + const u8 *pos = wpabuf_head_u8(buf); + const u8 *end = pos + wpabuf_len(buf); + + wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE"); + + while (pos < end) { + u16 attr_len; + if (pos + 2 >= end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); + return -1; + } + attr_len = WPA_GET_LE16(pos + 1); + wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", + pos[0], attr_len); + if (pos + 3 + attr_len > end) { + wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " + "(len=%u left=%d)", + attr_len, (int) (end - pos - 3)); + wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); + return -1; + } + if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) + return -1; + pos += 3 + attr_len; + } + + return 0; +} + + +static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + struct wps_parse_attr attr; + int i; + + wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE"); + if (wps_parse_msg(buf, &attr)) + return -1; + if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) && + !msg->device_name[0]) + os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len); + if (attr.config_methods) { + msg->wps_config_methods = + WPA_GET_BE16(attr.config_methods); + wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x", + msg->wps_config_methods); + } + if (attr.dev_password_id) { + msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); + wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", + msg->dev_password_id); + } + if (attr.primary_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + msg->wps_pri_dev_type = attr.primary_dev_type; + wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s", + wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype, + sizeof(devtype))); + } + if (attr.sec_dev_type_list) { + msg->wps_sec_dev_type_list = attr.sec_dev_type_list; + msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + msg->wps_vendor_ext[i] = attr.vendor_ext[i]; + msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i]; + } + + msg->manufacturer = attr.manufacturer; + msg->manufacturer_len = attr.manufacturer_len; + msg->model_name = attr.model_name; + msg->model_name_len = attr.model_name_len; + msg->model_number = attr.model_number; + msg->model_number_len = attr.model_number_len; + msg->serial_number = attr.serial_number; + msg->serial_number_len = attr.serial_number_len; + + return 0; +} + + +/** + * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE) + * @data: IEs from the message + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) +{ + struct ieee802_11_elems elems; + + ieee802_11_parse_elems(data, len, &elems, 0); + if (elems.ds_params && elems.ds_params_len >= 1) + msg->ds_params = elems.ds_params; + if (elems.ssid) + msg->ssid = elems.ssid - 2; + + msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len, + WPS_DEV_OUI_WFA); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len, + P2P_IE_VENDOR_TYPE); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + + return 0; +} + + +/** + * p2p_parse - Parse a P2P Action frame contents + * @data: Action frame payload after Category and Code fields + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + wpa_printf(MSG_DEBUG, "P2P: Parsing the received message"); + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message"); + return -1; + } + msg->dialog_token = data[0]; + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token); + + return p2p_parse_ies(data + 1, len - 1, msg); +} + + +/** + * p2p_parse_free - Free temporary data from P2P parsing + * @msg: Parsed attributes + */ +void p2p_parse_free(struct p2p_message *msg) +{ + wpabuf_free(msg->p2p_attributes); + msg->p2p_attributes = NULL; + wpabuf_free(msg->wps_attributes); + msg->wps_attributes = NULL; +} + + +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info) +{ + const u8 *g, *gend; + + os_memset(info, 0, sizeof(*info)); + if (gi == NULL) + return 0; + + g = gi; + gend = gi + gi_len; + while (g < gend) { + struct p2p_client_info *cli; + const u8 *t, *cend; + int count; + + cli = &info->client[info->num_clients]; + cend = g + 1 + g[0]; + if (cend > gend) + return -1; /* invalid data */ + /* g at start of P2P Client Info Descriptor */ + /* t at Device Capability Bitmap */ + t = g + 1 + 2 * ETH_ALEN; + if (t > cend) + return -1; /* invalid data */ + cli->p2p_device_addr = g + 1; + cli->p2p_interface_addr = g + 1 + ETH_ALEN; + cli->dev_capab = t[0]; + + if (t + 1 + 2 + 8 + 1 > cend) + return -1; /* invalid data */ + + cli->config_methods = WPA_GET_BE16(&t[1]); + cli->pri_dev_type = &t[3]; + + t += 1 + 2 + 8; + /* t at Number of Secondary Device Types */ + cli->num_sec_dev_types = *t++; + if (t + 8 * cli->num_sec_dev_types > cend) + return -1; /* invalid data */ + cli->sec_dev_types = t; + t += 8 * cli->num_sec_dev_types; + + /* t at Device Name in WPS TLV format */ + if (t + 2 + 2 > cend) + return -1; /* invalid data */ + if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + return -1; /* invalid Device Name TLV */ + t += 2; + count = WPA_GET_BE16(t); + t += 2; + if (count > cend - t) + return -1; /* invalid Device Name TLV */ + if (count >= 32) + count = 32; + cli->dev_name = (const char *) t; + cli->dev_name_len = count; + + g = cend; + + info->num_clients++; + if (info->num_clients == P2P_MAX_GROUP_ENTRIES) + return -1; + } + + return 0; +} + + +static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, + char *end) +{ + char *pos = buf; + int ret; + struct p2p_group_info info; + unsigned int i; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return 0; + + for (i = 0; i < info.num_clients; i++) { + struct p2p_client_info *cli; + char name[33]; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + u8 s; + int count; + + cli = &info.client[i]; + ret = os_snprintf(pos, end - pos, "p2p_group_client: " + "dev=" MACSTR " iface=" MACSTR, + MAC2STR(cli->p2p_device_addr), + MAC2STR(cli->p2p_interface_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + " dev_capab=0x%x config_methods=0x%x " + "dev_type=%s", + cli->dev_capab, cli->config_methods, + wps_dev_type_bin2str(cli->pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + for (s = 0; s < cli->num_sec_dev_types; s++) { + ret = os_snprintf(pos, end - pos, " dev_type=%s", + wps_dev_type_bin2str( + &cli->sec_dev_types[s * 8], + devtype, sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + os_memcpy(name, cli->dev_name, cli->dev_name_len); + name[cli->dev_name_len] = '\0'; + count = (int) cli->dev_name_len - 1; + while (count >= 0) { + if (name[count] > 0 && name[count] < 32) + name[count] = '_'; + count--; + } + + ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +/** + * p2p_attr_text - Build text format description of P2P IE attributes + * @data: P2P IE contents + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on faikure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_attr_text(struct wpabuf *data, char *buf, char *end) +{ + struct p2p_message msg; + char *pos = buf; + int ret; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(data, &msg)) + return -1; + + if (msg.capability) { + ret = os_snprintf(pos, end - pos, + "p2p_dev_capab=0x%x\n" + "p2p_group_capab=0x%x\n", + msg.capability[0], msg.capability[1]); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (msg.pri_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + ret = os_snprintf(pos, end - pos, + "p2p_primary_device_type=%s\n", + wps_dev_type_bin2str(msg.pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", + msg.device_name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (msg.p2p_device_addr) { + ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR + "\n", + MAC2STR(msg.p2p_device_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", + msg.config_methods); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = p2p_group_info_text(msg.group_info, msg.group_info_len, + pos, end); + if (ret < 0) + return pos - buf; + pos += ret; + + return pos - buf; +} + + +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.manageability) + return 0; + + return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED); +} + + +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.capability) + return 0; + + return msg.capability[1]; +} + + +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return NULL; + + if (msg.p2p_device_addr) + return msg.p2p_device_addr; + if (msg.device_id) + return msg.device_id; + + return NULL; +} diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c new file mode 100644 index 0000000..8eced26 --- /dev/null +++ b/src/p2p/p2p_pd.c @@ -0,0 +1,416 @@ +/* + * Wi-Fi Direct - P2P provision discovery + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +/* + * Number of retries to attempt for provision discovery requests during IDLE + * state in case the peer is not listening. + */ +#define MAX_PROV_DISC_REQ_RETRIES 10 + + +static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, + u16 config_methods) +{ + u8 *len; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + /* Config Methods */ + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, config_methods); + + p2p_buf_update_ie_hdr(buf, len); +} + + +static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods, + struct p2p_device *go) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_capability(buf, p2p->dev_capab, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + if (go) { + p2p_buf_add_group_id(buf, go->info.p2p_device_addr, + go->oper_ssid, go->oper_ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + + return buf; +} + + +static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + + return buf; +} + + +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + struct p2p_device *dev; + int freq; + int reject = 1; + struct wpabuf *resp; + + if (p2p_parse(data, len, &msg)) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Provision Discovery Request from " MACSTR + " with config methods 0x%x (freq=%d)", + MAC2STR(sa), msg.wps_config_methods, rx_freq); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request from " + "unknown peer " MACSTR, MAC2STR(sa)); + if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Provision Discovery Request add device " + "failed " MACSTR, MAC2STR(sa)); + } + } + + if (!(msg.wps_config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | + WPS_CONFIG_PUSHBUTTON))) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " + "Config Methods in Provision Discovery Request"); + goto out; + } + + if (dev) + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD); + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " requested us to show a PIN on display", MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " requested us to write its PIN using keypad", + MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } + + reject = 0; + +out: + resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, + reject ? 0 : msg.wps_config_methods); + if (resp == NULL) { + p2p_parse_free(&msg); + return; + } + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + } + + wpabuf_free(resp); + + if (!reject && p2p->cfg->prov_disc_req) { + const u8 *dev_addr = sa; + if (msg.p2p_device_addr) + dev_addr = msg.p2p_device_addr; + p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, + msg.wps_config_methods, + dev_addr, msg.pri_dev_type, + msg.device_name, msg.config_methods, + msg.capability ? msg.capability[0] : 0, + msg.capability ? msg.capability[1] : + 0, + msg.group_id, msg.group_id_len); + } + p2p_parse_free(&msg); +} + + +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *dev; + u16 report_config_methods = 0; + + if (p2p_parse(data, len, &msg)) + return; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received Provisioning Discovery Response from " MACSTR + " with config methods 0x%x", + MAC2STR(sa), msg.wps_config_methods); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || !dev->req_config_methods) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Provisioning Discovery Response from " + MACSTR " with no pending request", MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + if (dev->dialog_token != msg.dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore Provisioning Discovery Response with " + "unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + /* + * If the response is from the peer to whom a user initiated request + * was sent earlier, we reset that state info here. + */ + if (p2p->user_initiated_pd && + os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) + p2p_reset_pending_pd(p2p); + + if (msg.wps_config_methods != dev->req_config_methods) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " + "our Provisioning Discovery Request"); + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_REJECTED); + p2p_parse_free(&msg); + goto out; + } + + report_config_methods = dev->req_config_methods; + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD); + if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " accepted to show a PIN on display", MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + " accepted to write our PIN using keypad", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } + + /* Store the provisioning info */ + dev->wps_prov_info = msg.wps_config_methods; + + p2p_parse_free(&msg); + +out: + dev->req_config_methods = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (p2p->cfg->prov_disc_resp) + p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, + report_config_methods); +} + + +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq) +{ + struct wpabuf *req; + int freq; + + if (force_freq > 0) + freq = force_freq; + else + freq = dev->listen_freq > 0 ? dev->listen_freq : + dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send Provision Discovery Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cannot use PD with P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + /* TODO: use device discoverability request through GO */ + } + + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + req = p2p_build_prov_disc_req(p2p, dev->dialog_token, + dev->req_config_methods, + join ? dev : NULL); + if (req == NULL) + return -1; + + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); + p2p->pending_action_state = P2P_PENDING_PD; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + wpabuf_free(req); + return -1; + } + + os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); + + wpabuf_free(req); + return 0; +} + + +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) + dev = p2p_get_device_interface(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " + "Discovery Request destination " MACSTR + " not yet known", MAC2STR(peer_addr)); + return -1; + } + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " + "Request with " MACSTR " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + if (config_methods == 0) + return -1; + + /* Reset provisioning info */ + dev->wps_prov_info = 0; + + dev->req_config_methods = config_methods; + if (join) + dev->flags |= P2P_DEV_PD_FOR_JOIN; + else + dev->flags &= ~P2P_DEV_PD_FOR_JOIN; + + if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && + p2p->state != P2P_LISTEN_ONLY) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " + "operations; postpone Provision Discovery Request " + "with " MACSTR " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + return 0; + } + + /* + * We use the join param as a cue to differentiate between user + * initiated PD request and one issued during finds (internal). + */ + p2p->user_initiated_pd = !join; + + /* Also set some retries to attempt in case of IDLE state */ + if (p2p->user_initiated_pd && p2p->state == P2P_IDLE) + p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; + + return p2p_send_prov_disc_req(p2p, dev, join, force_freq); +} + + +void p2p_reset_pending_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN)) + continue; + if (!dev->req_config_methods) + continue; + if (dev->flags & P2P_DEV_PD_FOR_JOIN) + continue; + /* Reset the config methods of the device */ + dev->req_config_methods = 0; + } + + p2p->user_initiated_pd = 0; + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pd_retries = 0; +} diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c new file mode 100644 index 0000000..43b3a61 --- /dev/null +++ b/src/p2p/p2p_sd.c @@ -0,0 +1,889 @@ +/* + * Wi-Fi Direct - P2P service discovery + * Copyright (c) 2009, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "p2p_i.h" +#include "p2p.h" + + +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev) +{ + struct p2p_sd_query *q; + + if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) + return 0; /* peer does not support SD */ + + for (q = p2p->sd_queries; q; q = q->next) { + if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) + return q; + if (!q->for_all_peers && + os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == + 0) + return q; + } + + return NULL; +} + + +static int p2p_unlink_sd_query(struct p2p_data *p2p, + struct p2p_sd_query *query) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + prev = NULL; + while (q) { + if (q == query) { + if (prev) + prev->next = q->next; + else + p2p->sd_queries = q->next; + if (p2p->sd_query == query) + p2p->sd_query = NULL; + return 1; + } + prev = q; + q = q->next; + } + return 0; +} + + +static void p2p_free_sd_query(struct p2p_sd_query *q) +{ + if (q == NULL) + return; + wpabuf_free(q->tlvs); + os_free(q); +} + + +void p2p_free_sd_queries(struct p2p_data *p2p) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + p2p->sd_queries = NULL; + while (q) { + prev = q; + q = q->next; + p2p_free_sd_query(prev); + } +} + + +static struct wpabuf * p2p_build_sd_query(u16 update_indic, + struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs)); + if (buf == NULL) + return NULL; + + /* ANQP Query Request Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, + u8 dialog_token, int freq) +{ + struct wpabuf *req; + + req = gas_build_comeback_req(dialog_token); + if (req == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(req); +} + + +static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, + u16 comeback_delay, + u16 update_indic, + const struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + 100 + (tlvs ? wpabuf_len(tlvs) : 0)); + if (buf == NULL) + return NULL; + + if (tlvs) { + /* ANQP Query Response Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + } + + gas_anqp_set_len(buf); + + return buf; +} + + +static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, + u16 status_code, + u16 update_indic, + const u8 *data, size_t len, + u8 frag_id, u8 more, + u16 total_len) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, 0, 100 + len); + if (buf == NULL) + return NULL; + + if (frag_id == 0) { + /* ANQP Query Response Frame */ + wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ + wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + } + + wpabuf_put_data(buf, data, len); + gas_anqp_set_len(buf); + + return buf; +} + + +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct wpabuf *req; + int ret = 0; + struct p2p_sd_query *query; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: No Listen/Operating frequency known for the " + "peer " MACSTR " to send SD Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + query = p2p_pending_sd_req(p2p, dev); + if (query == NULL) + return -1; + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Start Service Discovery with " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + + req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); + if (req == NULL) + return -1; + + p2p->sd_peer = dev; + p2p->sd_query = query; + p2p->pending_action_state = P2P_PENDING_SD; + + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 5000) < 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + ret = -1; + } + + wpabuf_free(req); + + return ret; +} + + +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + int freq; + u16 update_indic; + + + if (p2p->cfg->sd_request == NULL) + return; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) + return; + + if (len < 1 + 2) + return; + + dialog_token = *pos++; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: GAS Initial Request from " MACSTR " (dialog token %u, " + "freq %d)", + MAC2STR(sa), dialog_token, rx_freq); + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Request length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, + update_indic, pos, end - pos); + /* the response will be indicated with a call to p2p_sd_response() */ +} + + +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs) +{ + struct wpabuf *resp; + + /* TODO: fix the length limit to match with the maximum frame length */ + if (wpabuf_len(resp_tlvs) > 1400) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long " + "enough to require fragmentation"); + if (p2p->sd_resp) { + /* + * TODO: Could consider storing the fragmented response + * separately for each peer to avoid having to drop old + * one if there is more than one pending SD query. + * Though, that would eat more memory, so there are + * also benefits to just using a single buffer. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " + "previous SD response"); + wpabuf_free(p2p->sd_resp); + } + os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); + p2p->sd_resp_dialog_token = dialog_token; + p2p->sd_resp = wpabuf_dup(resp_tlvs); + p2p->sd_resp_pos = 0; + p2p->sd_frag_id = 0; + resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, + 1, p2p->srv_update_indic, NULL); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits " + "in initial response"); + resp = p2p_build_sd_response(dialog_token, + WLAN_STATUS_SUCCESS, 0, + p2p->srv_update_indic, resp_tlvs); + } + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u16 comeback_delay; + u16 slen; + u16 update_indic; + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected GAS Initial Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GAS Initial Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 5 + 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Too short GAS Initial Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: dialog_token=%u status_code=%u comeback_delay=%u", + dialog_token, status_code, comeback_delay); + if (status_code) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Initial Response: %u", + *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Initial Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " + "Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", + slen); + if (pos + slen > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " + "Response data"); + return; + } + end = pos + slen; + + if (comeback_delay) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented " + "response - request fragments"); + if (p2p->sd_rx_resp) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " + "old SD reassembly buffer"); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Response length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic, + pos, end - pos); + p2p_continue_find(p2p); +} + + +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct wpabuf *resp; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u", + dialog_token); + if (dialog_token != p2p->sd_resp_dialog_token) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment for dialog token %u", dialog_token); + return; + } + + if (p2p->sd_resp == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment available"); + return; + } + if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " + "response fragment for " MACSTR, MAC2STR(sa)); + return; + } + + frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; + if (frag_len > 1400) { + frag_len = 1400; + more = 1; + } + resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, + p2p->srv_update_indic, + wpabuf_head_u8(p2p->sd_resp) + + p2p->sd_resp_pos, frag_len, + p2p->sd_frag_id, more, + wpabuf_len(p2p->sd_resp)); + if (resp == NULL) + return; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback " + "Response (frag_id %d more=%d frag_len=%d)", + p2p->sd_frag_id, more, (int) frag_len); + p2p->sd_frag_id++; + p2p->sd_resp_pos += frag_len; + + if (more) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes " + "remain to be sent", + (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); + } else { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of " + "SD response sent"); + wpabuf_free(p2p->sd_resp); + p2p->sd_resp = NULL; + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u8 frag_id; + u8 more_frags; + u16 comeback_delay; + u16 slen; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len); + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Ignore unexpected GAS Comeback Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 6 + 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Too short GAS Comeback Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + "comeback_delay=%u", + dialog_token, status_code, frag_id, more_frags, + comeback_delay); + /* TODO: check frag_id match */ + if (status_code) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unexpected IE in GAS Comeback Response: %u", + *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid IE in GAS Comeback Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " + "Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", + slen); + if (pos + slen > end) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " + "Response data"); + return; + } + if (slen == 0) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response " + "data"); + return; + } + end = pos + slen; + + if (p2p->sd_rx_resp) { + /* + * ANQP header is only included in the first fragment; rest of + * the fragments start with continue TLVs. + */ + goto skip_nqp_header; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response " + "length: %u", slen); + if (slen < 3 + 1) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Invalid ANQP Query Response length"); + return; + } + if (pos + 4 > end) + return; + + if (WPA_GET_BE24(pos) != OUI_WFA) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + p2p->sd_rx_update_indic = WPA_GET_LE16(pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic); + pos += 2; + +skip_nqp_header: + if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) + return; + wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly " + "buffer length: %u", + (unsigned int) wpabuf_len(p2p->sd_rx_resp)); + + if (more_frags) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments " + "remains"); + /* TODO: what would be a good size limit? */ + if (wpabuf_len(p2p->sd_rx_resp) > 64000) { + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long " + "SD response - drop it"); + return; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, + p2p->sd_rx_update_indic, + wpabuf_head(p2p->sd_rx_resp), + wpabuf_len(p2p->sd_rx_resp)); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + + p2p_continue_find(p2p); +} + + +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + + q = os_zalloc(sizeof(*q)); + if (q == NULL) + return NULL; + + if (dst) + os_memcpy(q->peer, dst, ETH_ALEN); + else + q->for_all_peers = 1; + + q->tlvs = wpabuf_dup(tlvs); + if (q->tlvs == NULL) { + p2p_free_sd_query(q); + return NULL; + } + + q->next = p2p->sd_queries; + p2p->sd_queries = q; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); + + return q; +} + + +void p2p_sd_service_update(struct p2p_data *p2p) +{ + p2p->srv_update_indic++; +} + + +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) +{ + if (p2p_unlink_sd_query(p2p, req)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Cancel pending SD query %p", req); + p2p_free_sd_query(req); + return 0; + } + return -1; +} diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c new file mode 100644 index 0000000..da4b6ed --- /dev/null +++ b/src/p2p/p2p_utils.c @@ -0,0 +1,271 @@ +/* + * P2P - generic helper functions + * Copyright (c) 2009, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p_i.h" + + +/** + * p2p_random - Generate random string for SSID and passphrase + * @buf: Buffer for returning the result + * @len: Number of octets to write to the buffer + * Returns: 0 on success, -1 on failure + * + * This function generates a random string using the following character set: + * 'A'-'Z', 'a'-'z', '0'-'9'. + */ +int p2p_random(char *buf, size_t len) +{ + u8 val; + size_t i; + u8 letters = 'Z' - 'A' + 1; + u8 numbers = 10; + + if (os_get_random((unsigned char *) buf, len)) + return -1; + /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ + for (i = 0; i < len; i++) { + val = buf[i]; + val %= 2 * letters + numbers; + if (val < letters) + buf[i] = 'A' + val; + else if (val < 2 * letters) + buf[i] = 'a' + (val - letters); + else + buf[i] = '0' + (val - 2 * letters); + } + + return 0; +} + + +static int p2p_channel_to_freq_j4(int reg_class, int channel) +{ + /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ + /* TODO: more regulatory classes */ + switch (reg_class) { + case 81: + /* channels 1..13 */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 82: + /* channel 14 */ + if (channel != 14) + return -1; + return 2414 + 5 * channel; + case 83: /* channels 1..9; 40 MHz */ + case 84: /* channels 5..13; 40 MHz */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 115: /* channels 36,40,44,48; indoor only */ + case 118: /* channels 52,56,60,64; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 124: /* channels 149,153,157,161 */ + case 125: /* channels 149,153,157,161,165,169 */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 116: /* channels 36,44; 40 MHz; indoor only */ + case 117: /* channels 40,48; 40 MHz; indoor only */ + case 119: /* channels 52,60; 40 MHz; dfs */ + case 120: /* channels 56,64; 40 MHz; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 126: /* channels 149,157; 40 MHz */ + case 127: /* channels 153,161; 40 MHz */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + } + return -1; +} + + +/** + * p2p_channel_to_freq - Convert channel info to frequency + * @country: Country code + * @reg_class: Regulatory class + * @channel: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int p2p_channel_to_freq(const char *country, int reg_class, int channel) +{ + if (country[2] == 0x04) + return p2p_channel_to_freq_j4(reg_class, channel); + + /* These are mainly for backwards compatibility; to be removed */ + switch (reg_class) { + case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ + if (channel < 36 || channel > 48) + return -1; + return 5000 + 5 * channel; + case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ + case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ + case 12: /* US/12 = 2.407 GHz, channels 1..11 */ + case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 31: /* JP/31 = 2.414 GHz, channel 14 */ + if (channel != 14) + return -1; + return 2414 + 5 * channel; + } + + return -1; +} + + +/** + * p2p_freq_to_channel - Convert frequency into channel info + * @country: Country code + * @reg_class: Buffer for returning regulatory class + * @channel: Buffer for returning channel number + * Returns: 0 on success, -1 if the specified frequency is unknown + */ +int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, + u8 *channel) +{ + /* TODO: more operating classes */ + if (freq >= 2412 && freq <= 2472) { + *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + *channel = (freq - 2407) / 5; + return 0; + } + + if (freq == 2484) { + *reg_class = 82; /* channel 14 */ + *channel = 14; + return 0; + } + + if (freq >= 5180 && freq <= 5240) { + *reg_class = 115; /* 5 GHz, channels 36..48 */ + *channel = (freq - 5000) / 5; + return 0; + } + + if (freq >= 5745 && freq <= 5805) { + *reg_class = 124; /* 5 GHz, channels 149..161 */ + *channel = (freq - 5000) / 5; + return 0; + } + + return -1; +} + + +static void p2p_reg_class_intersect(const struct p2p_reg_class *a, + const struct p2p_reg_class *b, + struct p2p_reg_class *res) +{ + size_t i, j; + + res->reg_class = a->reg_class; + + for (i = 0; i < a->channels; i++) { + for (j = 0; j < b->channels; j++) { + if (a->channel[i] != b->channel[j]) + continue; + res->channel[res->channels] = a->channel[i]; + res->channels++; + if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + } + } +} + + +/** + * p2p_channels_intersect - Intersection of supported channel lists + * @a: First set of supported channels + * @b: Second set of supported channels + * @res: Data structure for returning the intersection of support channels + * + * This function can be used to find a common set of supported channels. Both + * input channels sets are assumed to use the same country code. If different + * country codes are used, the regulatory class numbers may not be matched + * correctly and results are undefined. + */ +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + size_t i, j; + + os_memset(res, 0, sizeof(*res)); + + for (i = 0; i < a->reg_classes; i++) { + const struct p2p_reg_class *a_reg = &a->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_reg = &b->reg_class[j]; + if (a_reg->reg_class != b_reg->reg_class) + continue; + p2p_reg_class_intersect( + a_reg, b_reg, + &res->reg_class[res->reg_classes]); + if (res->reg_class[res->reg_classes].channels) { + res->reg_classes++; + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + } + } + } +} + + +/** + * p2p_channels_includes - Check whether a channel is included in the list + * @channels: List of supported channels + * @reg_class: Regulatory class of the channel to search + * @channel: Channel number of the channel to search + * Returns: 1 if channel was found or 0 if not + */ +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + if (reg->reg_class != reg_class) + continue; + for (j = 0; j < reg->channels; j++) { + if (reg->channel[j] == channel) + return 1; + } + } + return 0; +} + + +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(p2p->cfg->country, freq, + &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel); +} diff --git a/src/radius/radius.c b/src/radius/radius.c index 70754ef..fb03a25 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -1090,8 +1090,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *secret, size_t secret_len) { u8 buf[128]; - int padlen, i; - size_t buf_len, pos; + size_t padlen, i, buf_len, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; @@ -1103,7 +1102,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, buf_len = data_len; padlen = data_len % 16; - if (padlen) { + if (padlen && data_len < sizeof(buf)) { padlen = 16 - padlen; os_memset(buf + data_len, 0, padlen); buf_len += padlen; diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 171af29..9ec95a2 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -684,7 +684,7 @@ int radius_client_send(struct radius_client_data *radius, radius_client_list_add(radius, msg, msg_type, shared_secret, shared_secret_len, addr); - return res; + return 0; } @@ -1489,3 +1489,11 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf, return count; } + + +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf) +{ + if (radius) + radius->conf = conf; +} diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h index 644ea23..18e7290 100644 --- a/src/radius/radius_client.h +++ b/src/radius/radius_client.h @@ -259,5 +259,7 @@ void radius_client_flush_auth(struct radius_client_data *radius, const u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen); +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf); #endif /* RADIUS_CLIENT_H */ diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index f8780a6..6f1c3a5 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -222,6 +222,13 @@ struct radius_server_data { int tnc; /** + * pwd_group - The D-H group assigned for EAP-pwd + * + * If EAP-pwd is not used it can be set to zero. + */ + u16 pwd_group; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -505,6 +512,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; + eap_conf.pwd_group = data->pwd_group; sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -1259,6 +1267,7 @@ radius_server_init(struct radius_server_conf *conf) data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; data->tnc = conf->tnc; data->wps = conf->wps; + data->pwd_group = conf->pwd_group; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index f9c951d..126e314 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -143,6 +143,13 @@ struct radius_server_conf { int tnc; /** + * pwd_group - EAP-pwd D-H group + * + * This is used to select which D-H group to use with EAP-pwd. + */ + u16 pwd_group; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index 9d60d4a..2b3332e 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -20,6 +20,7 @@ #include "eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "wpa.h" #include "wpa_i.h" @@ -254,7 +255,7 @@ static int wpa_supplicant_process_smk_m2( peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for PNonce"); wpa_supplicant_peerkey_free(sm, peerkey); @@ -370,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); - if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for INonce (STK)"); os_free(mbuf); @@ -697,7 +698,7 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, return; } - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; @@ -1097,7 +1098,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for INonce"); os_free(rbuf); @@ -1140,6 +1141,7 @@ void peerkey_deinit(struct wpa_sm *sm) peerkey = peerkey->next; os_free(prev); } + sm->peerkey = NULL; } diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index cac8c83..3877efb 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -49,6 +49,7 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, int replace) { + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx, replace); _pmksa_cache_free_entry(entry); @@ -185,6 +186,14 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " "the current AP"); pmksa_cache_free_entry(pmksa, pos, 1); + + /* + * If OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. + */ + pmksa_cache_flush(pmksa, network_ctx); break; } prev = pos; @@ -198,7 +207,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " "entry (for " MACSTR ") to make room for new one", MAC2STR(pos->aa)); - wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid); pmksa_cache_free_entry(pmksa, pos, 0); } @@ -229,6 +237,39 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, /** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if (entry->network_ctx == network_ctx || network_ctx == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, 0); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + if (removed) + pmksa_cache_set_expiration(pmksa); +} + + +/** * pmksa_cache_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() */ @@ -273,22 +314,6 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, } -/** - * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() - * - * Clear references to old data structures when wpa_supplicant is reconfigured. - */ -void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) -{ - struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; - while (entry) { - entry->network_ctx = NULL; - entry = entry->next; - } -} - - static struct rsn_pmksa_cache_entry * pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, @@ -327,6 +352,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); if (network_ctx == NULL) return NULL; while (entry) { diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index a1447e5..840827d 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -57,7 +57,6 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); -void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, @@ -66,6 +65,7 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx); #else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ @@ -106,10 +106,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, return NULL; } -static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa) -{ -} - static inline void pmksa_cache_clear_current(struct wpa_sm *sm) { } @@ -122,6 +118,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, return -1; } +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx) +{ +} + #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ #endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c new file mode 100644 index 0000000..27090e3 --- /dev/null +++ b/src/rsn_supp/tdls.c @@ -0,0 +1,2338 @@ +/* + * wpa_supplicant - TDLS + * Copyright (c) 2010-2011, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/os.h" +#include "common/ieee802_11_defs.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "crypto/aes_wrap.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" +#include "rsn_supp/wpa_i.h" +#include "drivers/driver.h" +#include "l2_packet/l2_packet.h" + +#ifdef CONFIG_TDLS_TESTING +#define TDLS_TESTING_LONG_FRAME BIT(0) +#define TDLS_TESTING_ALT_RSN_IE BIT(1) +#define TDLS_TESTING_DIFF_BSSID BIT(2) +#define TDLS_TESTING_SHORT_LIFETIME BIT(3) +#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4) +#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5) +#define TDLS_TESTING_LONG_LIFETIME BIT(6) +#define TDLS_TESTING_CONCURRENT_INIT BIT(7) +#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) +#define TDLS_TESTING_DECLINE_RESP BIT(9) +#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) +unsigned int tdls_testing = 0; +#endif /* CONFIG_TDLS_TESTING */ + +#define TPK_LIFETIME 43200 /* 12 hours */ +#define TPK_RETRY_COUNT 3 +#define TPK_TIMEOUT 5000 /* in milliseconds */ + +#define TDLS_MIC_LEN 16 + +#define TDLS_TIMEOUT_LEN 4 + +struct wpa_tdls_ftie { + u8 ie_type; /* FTIE */ + u8 ie_len; + u8 mic_ctrl[2]; + u8 mic[TDLS_MIC_LEN]; + u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */ + u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */ + /* followed by optional elements */ +} STRUCT_PACKED; + +struct wpa_tdls_timeoutie { + u8 ie_type; /* Timeout IE */ + u8 ie_len; + u8 interval_type; + u8 value[TDLS_TIMEOUT_LEN]; +} STRUCT_PACKED; + +struct wpa_tdls_lnkid { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[ETH_ALEN]; + u8 init_sta[ETH_ALEN]; + u8 resp_sta[ETH_ALEN]; +} STRUCT_PACKED; + +/* TDLS frame headers as per IEEE Std 802.11z-2010 */ +struct wpa_tdls_frame { + u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */ + u8 category; /* Category */ + u8 action; /* Action (enum tdls_frame_type) */ +} STRUCT_PACKED; + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); + + +#define TDLS_MAX_IE_LEN 80 +#define IEEE80211_MAX_SUPP_RATES 32 + +struct wpa_tdls_peer { + struct wpa_tdls_peer *next; + int initiator; /* whether this end was initiator for TDLS setup */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */ + u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u32 lifetime; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 dtoken; + + struct tpk { + u8 kck[16]; /* TPK-KCK */ + u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ + } tpk; + int tpk_set; + int tpk_success; + + struct tpk_timer { + u8 dest[ETH_ALEN]; + int count; /* Retry Count */ + int timer; /* Timeout in milliseconds */ + u8 action_code; /* TDLS frame type */ + u8 dialog_token; + u16 status_code; + int buf_len; /* length of TPK message for retransmission */ + u8 *buf; /* buffer for TPK message */ + } sm_tmr; + + u16 capability; + + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; +}; + + +static int wpa_tdls_get_privacy(struct wpa_sm *sm) +{ + /* + * Get info needed from supplicant to check if the current BSS supports + * security. Other than OPEN mode, rest are considered secured + * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake. + */ + return sm->pairwise_cipher != WPA_CIPHER_NONE; +} + + +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) +{ + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr, + 0, 0, NULL, 0, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from " + "the driver"); + return -1; + } + + return 0; +} + + +static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + u8 key_len; + u8 rsc[6]; + enum wpa_alg alg; + + os_memset(rsc, 0, 6); + + switch (peer->cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + key_len = 16; + break; + case WPA_CIPHER_NONE: + wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + return -1; + default: + wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, + rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " + "driver"); + return -1; + } + return 0; +} + + +static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len) +{ + return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, + status_code, buf, len); +} + + +static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *msg, size_t msg_len) +{ + struct wpa_tdls_peer *peer; + + wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " + "dialog_token=%u status_code=%u msg_len=%u", + MAC2STR(dest), action_code, dialog_token, status_code, + (unsigned int) msg_len); + + if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, + status_code, msg, msg_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to send message " + "(action_code=%u)", action_code); + return -1; + } + + if (action_code == WLAN_TDLS_SETUP_CONFIRM || + action_code == WLAN_TDLS_TEARDOWN || + action_code == WLAN_TDLS_DISCOVERY_REQUEST || + action_code == WLAN_TDLS_DISCOVERY_RESPONSE) + return 0; /* No retries */ + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "retry " MACSTR, MAC2STR(dest)); + return 0; + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + peer->sm_tmr.count = TPK_RETRY_COUNT; + peer->sm_tmr.timer = TPK_TIMEOUT; + + /* Copy message to resend on timeout */ + os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); + peer->sm_tmr.action_code = action_code; + peer->sm_tmr.dialog_token = dialog_token; + peer->sm_tmr.status_code = status_code; + peer->sm_tmr.buf_len = msg_len; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = os_malloc(msg_len); + if (peer->sm_tmr.buf == NULL) + return -1; + os_memcpy(peer->sm_tmr.buf, msg, msg_len); + + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " + "(action_code=%u)", action_code); + eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + wpa_tdls_tpk_retry_timeout, sm, peer); + return 0; +} + + +static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + u16 reason_code, int free_peer) +{ + int ret; + + if (sm->tdls_external_setup) { + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + + /* disable the link after teardown was sent */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + } else { + ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + } + + if (sm->tdls_external_setup || free_peer) + wpa_tdls_peer_free(sm, peer); + + return ret; +} + + +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + if (peer->sm_tmr.count) { + peer->sm_tmr.count--; + peer->sm_tmr.timer = TPK_TIMEOUT; + + wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " + "(action_code=%u)", + peer->sm_tmr.action_code); + + if (peer->sm_tmr.buf == NULL) { + wpa_printf(MSG_INFO, "TDLS: No retry buffer available " + "for action_code=%u", + peer->sm_tmr.action_code); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, + peer); + return; + } + + /* resend TPK Handshake Message to Peer */ + if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest, + peer->sm_tmr.action_code, + peer->sm_tmr.dialog_token, + peer->sm_tmr.status_code, + peer->sm_tmr.buf, + peer->sm_tmr.buf_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to retry " + "transmission"); + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + wpa_tdls_tpk_retry_timeout, sm, peer); + } else { + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + } +} + + +static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 action_code) +{ + if (action_code == peer->sm_tmr.action_code) { + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for " + "action_code=%u", action_code); + + /* Cancel Timeout registered */ + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + /* free all resources meant for retry */ + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + + peer->sm_tmr.count = 0; + peer->sm_tmr.timer = 0; + peer->sm_tmr.buf_len = 0; + peer->sm_tmr.action_code = 0xff; + } else { + wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout " + "(Unknown action_code=%u)", action_code); + } +} + + +static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, + const u8 *own_addr, const u8 *bssid) +{ + u8 key_input[SHA256_MAC_LEN]; + const u8 *nonce[2]; + size_t len[2]; + u8 data[3 * ETH_ALEN]; + + /* IEEE Std 802.11z-2010 8.5.9.1: + * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) + */ + len[0] = WPA_NONCE_LEN; + len[1] = WPA_NONCE_LEN; + if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) { + nonce[0] = peer->inonce; + nonce[1] = peer->rnonce; + } else { + nonce[0] = peer->rnonce; + nonce[1] = peer->inonce; + } + wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN); + sha256_vector(2, nonce, len, key_input); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", + key_input, SHA256_MAC_LEN); + + /* + * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", + * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) + * TODO: is N_KEY really included in KDF Context and if so, in which + * presentation format (little endian 16-bit?) is it used? It gets + * added by the KDF anyway.. + */ + + if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { + os_memcpy(data, own_addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN); + } else { + os_memcpy(data, peer->addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN); + } + os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); + + sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), + (u8 *) &peer->tpk, sizeof(peer->tpk)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", + peer->tpk.kck, sizeof(peer->tpk.kck)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", + peer->tpk.tk, sizeof(peer->tpk.tk)); + peer->tpk_set = 1; +} + + +/** + * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC + * @kck: TPK-KCK + * @lnkid: Pointer to the beginning of Link Identifier IE + * @rsnie: Pointer to the beginning of RSN IE used for handshake + * @timeoutie: Pointer to the beginning of Timeout IE used for handshake + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid, + const u8 *rsnie, const u8 *timeoutie, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + const struct wpa_tdls_lnkid *_lnkid; + int ret; + int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] + + 2 + timeoutie[1] + 2 + ftie[1]; + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + _lnkid = (const struct wpa_tdls_lnkid *) lnkid; + /* 1) TDLS initiator STA MAC address */ + os_memcpy(pos, _lnkid->init_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 2) TDLS responder STA MAC address */ + os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 3) Transaction Sequence number */ + *pos++ = trans_seq; + /* 4) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 5) RSN IE */ + os_memcpy(pos, rsnie, 2 + rsnie[1]); + pos += 2 + rsnie[1]; + /* 6) Timeout Interval IE */ + os_memcpy(pos, timeoutie, 2 + timeoutie[1]); + pos += 2 + timeoutie[1]; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +/** + * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame + * @kck: TPK-KCK + * @trans_seq: Transaction Sequence Number (4 - Teardown) + * @rcode: Reason code for Teardown + * @dtoken: Dialog Token used for that particular link + * @lnkid: Pointer to the beginning of Link Identifier IE + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode, + u8 dtoken, const u8 *lnkid, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + int ret; + int len; + + if (lnkid == NULL) + return -1; + + len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) + + sizeof(trans_seq) + 2 + ftie[1]; + + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + /* 1) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 2) Reason Code */ + WPA_PUT_LE16(pos, rcode); + pos += sizeof(rcode); + /* 3) Dialog token */ + *pos++ = dtoken; + /* 4) Transaction Sequence number */ + *pos++ = trans_seq; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, + struct wpa_tdls_peer *peer, + const u8 *lnkid, const u8 *timeoutie, + const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, + peer->rsnie_p, timeoutie, (u8 *) ftie, + mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " + "dropping packet"); + wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", + ftie->mic, 16); + wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC", + mic, 16); + return -1; + } + } else { + wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, " + "TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static int wpa_supplicant_verify_tdls_mic_teardown( + u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer, + const u8 *lnkid, const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, + dtoken, lnkid, (u8 *) ftie, mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " + "dropping packet"); + return -1; + } + } else { + wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown " + "MIC, TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + /* + * On TPK lifetime expiration, we have an option of either tearing down + * the direct link or trying to re-initiate it. The selection of what + * to do is not strictly speaking controlled by our role in the expired + * link, but for now, use that to select whether to renew or tear down + * the link. + */ + + if (peer->initiator) { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - try to renew", MAC2STR(peer->addr)); + wpa_tdls_start(sm, peer->addr); + } else { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - tear down", MAC2STR(peer->addr)); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + } +} + + +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, + MAC2STR(peer->addr)); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->initiator = 0; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + peer->rsnie_i_len = peer->rsnie_p_len = 0; + peer->cipher = 0; + peer->tpk_set = peer->tpk_success = 0; + os_memset(&peer->tpk, 0, sizeof(peer->tpk)); + os_memset(peer->inonce, 0, WPA_NONCE_LEN); + os_memset(peer->rnonce, 0, WPA_NONCE_LEN); +} + + +static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + struct wpa_tdls_lnkid *lnkid) +{ + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = 3 * ETH_ALEN; + os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN); + if (peer->initiator) { + os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN); + } else { + os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN); + } +} + + +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid lnkid; + u8 dialog_token; + u8 *rbuf, *pos; + int ielen; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(addr)); + return 0; + } + + dialog_token = peer->dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, + MAC2STR(addr)); + + ielen = 0; + if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) { + /* To add FTIE for Teardown request and compute MIC */ + ielen += sizeof(*ftie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + ielen += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(ielen + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ies; + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* Using the recent nonce which should be for CONFIRM frame */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + pos = (u8 *) (ftie + 1); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake", + (u8 *) ftie, pos - (u8 *) ftie); + + /* compute MIC before sending */ + wpa_tdls_linkid(sm, peer, &lnkid); + wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code, + dialog_token, (u8 *) &lnkid, (u8 *) ftie, + ftie->mic); + +skip_ies: + /* TODO: register for a Timeout handler, if Teardown is not received at + * the other end, then try again another time */ + + /* request driver to send Teardown using this FTIE */ + wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, + pos - rbuf); + os_free(rbuf); + + /* clear the Peerkey statemachine */ + wpa_tdls_peer_free(sm, peer); + + return 0; +} + + +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR + " for link Teardown", MAC2STR(addr)); + return -1; + } + + if (!peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " not connected - cannot Teardown link", MAC2STR(addr)); + return -1; + } + + return wpa_tdls_do_teardown(sm, peer, reason_code, 0); +} + + +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer) { + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); + wpa_tdls_peer_free(sm, peer); + } +} + + +static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer = NULL; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid *lnkid; + struct wpa_eapol_ie_parse kde; + u16 reason_code; + const u8 *pos; + int ielen; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(src_addr)); + return 0; + } + + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + reason_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR + " (reason code %u)", MAC2STR(src_addr), reason_code); + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); + return -1; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " + "Teardown"); + return -1; + } + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ftie; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown"); + return -1; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + /* Process MIC check to see if TDLS Teardown is right */ + if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code, + peer->dtoken, peer, + (u8 *) lnkid, ftie) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS " + "Teardown Request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + +skip_ftie: + /* + * Request the driver to disable the direct link and clear associated + * keys. + */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + + /* clear the Peerkey statemachine */ + wpa_tdls_peer_free(sm, peer); + + return 0; +} + + +/** + * wpa_tdls_send_error - To send suitable TDLS status response with + * appropriate status code mentioning reason for error/failure. + * @dst - MAC addr of Peer station + * @tdls_action - TDLS frame type for which error code is sent + * @status - status code mentioning reason + */ + +static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, + u8 tdls_action, u8 dialog_token, u16 status) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR + " (action=%u status=%u)", + MAC2STR(dst), tdls_action, status); + return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, + NULL, 0); +} + + +static struct wpa_tdls_peer * +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, + MAC2STR(addr)); + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) + return NULL; + + os_memcpy(peer->addr, addr, ETH_ALEN); + peer->next = sm->tdls; + sm->tdls = peer; + + return peer; +} + + +static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + size_t buf_len; + struct wpa_tdls_timeoutie timeoutie; + u16 rsn_capab; + struct wpa_tdls_ftie *ftie; + u8 *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + + if (!wpa_tdls_get_privacy(sm)) { + wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); + peer->rsnie_i_len = 0; + goto skip_rsnie; + } + + /* + * TPK Handshake Message 1: + * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I, + * Timeout Interval IE)) + */ + + /* Filling RSN IE */ + hdr = (struct rsn_ie_hdr *) peer->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + + pos = (u8 *) (hdr + 1); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = 0; + + /* + * AES-CCMP is the default Encryption preferred for TDLS, so + * RSN IE is filled only with CCMP CIPHER + * Note: TKIP is not used to encrypt TDLS link. + * + * Regardless of the cipher used on the AP connection, select CCMP + * here. + */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + + WPA_PUT_LE16(count_pos, count); + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for " + "testing"); + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + } +#endif /* CONFIG_TDLS_TESTING */ + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + /* Number of PMKIDs */ + *pos++ = 0x00; + *pos++ = 0x00; + } +#endif /* CONFIG_TDLS_TESTING */ + + hdr->len = (pos - peer->rsnie_i) - 2; + peer->rsnie_i_len = pos - peer->rsnie_i; + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_i, peer->rsnie_i_len); + +skip_rsnie: + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (wpa_tdls_get_privacy(sm) && + (tdls_testing & TDLS_TESTING_LONG_FRAME)) + buf_len += 170; + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) + buf_len += sizeof(struct wpa_tdls_lnkid); +#endif /* CONFIG_TDLS_TESTING */ + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) { + wpa_tdls_peer_free(sm, peer); + return -1; + } + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + if (os_get_random(peer->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "TDLS: Failed to get random data for initiator Nonce"); + os_free(rbuf); + wpa_tdls_peer_free(sm, peer); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", + peer->inonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1", + (u8 *) ftie, sizeof(struct wpa_tdls_ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + peer->lifetime = TPK_LIFETIME; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK " + "lifetime"); + peer->lifetime = 301; + } + if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK " + "lifetime"); + peer->lifetime = 0xffffffff; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), peer->lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); + +skip_ies: + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " + "Link Identifier"); + struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; + wpa_tdls_linkid(sm, peer, l); + l->bssid[5] ^= 0x01; + pos += sizeof(*l); + } +#endif /* CONFIG_TDLS_TESTING */ + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK " + "Handshake Message 1 (peer " MACSTR ")", + MAC2STR(peer->addr)); + + wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + u32 lifetime; + struct wpa_tdls_timeoutie timeoutie; + struct wpa_tdls_ftie *ftie; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* TODO: ftie->mic_control to set 2-RESPONSE */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2", + (u8 *) ftie, sizeof(*ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in response"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie timeoutie; + u32 lifetime; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /*TODO: ftie->mic_control to set 3-CONFIRM */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in confirm"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, + rbuf, pos - rbuf); + os_free(rbuf); + + return 0; +} + + +static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 dialog_token) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " + "(peer " MACSTR ")", MAC2STR(peer->addr)); + + return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, + dialog_token, 0, NULL, 0); +} + + +static int +wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, + const u8 *buf, size_t len) +{ + struct wpa_eapol_ie_parse kde; + const struct wpa_tdls_lnkid *lnkid; + struct wpa_tdls_peer *peer; + size_t min_req_len = sizeof(struct wpa_tdls_frame) + + 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid); + u8 dialog_token; + + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR, + MAC2STR(addr)); + + if (len < min_req_len) { + wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: " + "%d", (int) len); + return -1; + } + + dialog_token = buf[sizeof(struct wpa_tdls_frame)]; + + if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, + len - (sizeof(struct wpa_tdls_frame) + 1), + &kde) < 0) + return -1; + + if (!kde.lnkid) { + wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " + "Request"); + return -1; + } + + lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different " + " BSS " MACSTR, MAC2STR(lnkid->bssid)); + return -1; + } + + peer = wpa_tdls_add_peer(sm, addr); + if (peer == NULL) + return -1; + + return wpa_tdls_send_discovery_response(sm, peer, dialog_token); +} + + +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " + MACSTR, MAC2STR(addr)); + return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, + 1, 0, NULL, 0); +} + + +static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_rates) { + wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); + return -1; + } + + peer->supp_rates_len = kde->supp_rates_len - 2; + if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) + peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; + os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); + + if (kde->ext_supp_rates) { + int clen = kde->ext_supp_rates_len - 2; + if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) + clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; + os_memcpy(peer->supp_rates + peer->supp_rates_len, + kde->ext_supp_rates + 2, clen); + peer->supp_rates_len += clen; + } + + return 0; +} + + +static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + const u8 *cpos; + struct wpa_tdls_ftie *ftie = NULL; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime = 0; +#if 0 + struct rsn_ie_hdr *hdr; + u8 *pos; + u16 rsn_capab; + u16 rsn_ver; +#endif + u8 dtoken; + u16 ielen; + u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; + int tdls_prohibited = sm->tdls_prohibited; + int existing_peer = 0; + + if (len < 3 + 3) + return -1; + + cpos = buf; + cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + /* driver had already verified the frame format */ + dtoken = *cpos++; /* dialog token */ + + wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { + existing_peer = 1; + break; + } + } + + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, src_addr); + if (peer == NULL) + goto error; + } + + /* capability information */ + peer->capability = WPA_GET_LE16(cpos); + cpos += 2; + + ielen = len - (cpos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); + goto error; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M1"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, + MAC2STR(src_addr)); + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, src_addr); + if (peer == NULL) + goto error; + } + wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " + "TDLS setup - send own request"); + peer->initiator = 1; + wpa_tdls_send_tpk_m1(sm, peer); + } + + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } + + if (!wpa_tdls_get_privacy(sm)) { + if (kde.rsn_ie) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while " + "security is disabled"); + status = WLAN_STATUS_SECURITY_DISABLED; + goto error; + } + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + + if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher & WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + if ((ie.capabilities & + (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) != + WPA_CAPABILITY_PEERKEY_ENABLED) { + wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; + goto error; + } + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); + if (lifetime < 300) { + wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + +skip_rsn: + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); +#if 0 + /* TODO: Disabling the link would be more proper + * operation here, but it seems to trigger a race with + * some drivers handling the new request frame. */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); +#else + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, + src_addr); + else + wpa_tdls_del_key(sm, peer); +#endif + wpa_tdls_peer_free(sm, peer); + } + + /* + * An entry is already present, so check if we already sent a + * TDLS Setup Request. If so, compare MAC addresses and let the + * STA with the lower MAC address continue as the initiator. + * The other negotiation is terminated. + */ + if (peer->initiator) { + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_tdls_peer_free(sm, peer); + } + } + } + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { + /* + * The request frame from us is going to win, so do not + * replace information based on this request frame from + * the peer. + */ + goto skip_rsn_check; + } + } +#endif /* CONFIG_TDLS_TESTING */ + + peer->initiator = 0; /* Need to check */ + peer->dtoken = dtoken; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_i_len = 0; + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn_check; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_i_len = kde.rsn_ie_len; + peer->cipher = cipher; + + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + wpa_tdls_peer_free(sm, peer); + goto error; + } + +#if 0 + /* get version info from RSNIE received from Peer */ + hdr = (struct rsn_ie_hdr *) kde.rsn_ie; + rsn_ver = WPA_GET_LE16(hdr->version); + + /* use min(peer's version, out version) */ + if (rsn_ver > RSN_VERSION) + rsn_ver = RSN_VERSION; + + hdr = (struct rsn_ie_hdr *) peer->rsnie_p; + + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, rsn_ver); + pos = (u8 *) (hdr + 1); + + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + /* Include only the selected cipher in pairwise cipher suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + if (cipher == WPA_CIPHER_CCMP) + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + + hdr->len = (pos - peer->rsnie_p) - 2; + peer->rsnie_p_len = pos - peer->rsnie_p; +#endif + + /* temp fix: validation of RSNIE later */ + os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len); + peer->rsnie_p_len = peer->rsnie_i_len; + + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_p, peer->rsnie_p_len); + + peer->lifetime = lifetime; + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + +skip_rsn_check: + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); + if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + goto error; + } + + return 0; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + status); + return -1; +} + + +static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + peer->tpk_success = 1; + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + if (wpa_tdls_get_privacy(sm)) { + u32 lifetime = peer->lifetime; + /* + * Start the initiator process a bit earlier to avoid race + * condition with the responder sending teardown request. + */ + if (lifetime > 3 && peer->initiator) + lifetime -= 3; + eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, + sm, peer); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " + "expiration"); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + } +#endif /* CONFIG_TDLS_TESTING */ + } + + /* add supported rates and capabilities to the TDLS peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, + peer->supp_rates, peer->supp_rates_len); + + wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); +} + + +static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime; + u8 dtoken; + int ielen; + u16 status; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M2: " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); + + if (len < 3 + 2 + 1) + return -1; + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + status = WPA_GET_LE16(pos); + pos += 2 /* status code */; + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* TODO: need to verify dialog token matches here or in kernel */ + dtoken = *pos++; /* dialog token */ + + wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); + + if (len < 3 + 2 + 1 + 2) + return -1; + + /* capability information */ + peer->capability = WPA_GET_LE16(pos); + pos += 2; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); + goto error; + } + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M2"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + + /* + * FIX: bitwise comparison of RSN IE is not the correct way of + * validation this. It can be different, but certain fields must + * match. Since we list only a single pairwise cipher in TPK M1, the + * memcmp is likely to work in most cases, though. + */ + if (kde.rsn_ie_len != peer->rsnie_i_len || + os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does " + "not match with RSN IE used in TPK M1"); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1", + peer->rsnie_i, peer->rsnie_i_len); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher == WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " + "not match with FTIE SNonce used in TPK M1"); + /* Silently discard the frame */ + return -1; + } + + /* Responder Nonce and RSN IE */ + os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN); + os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_p_len = kde.rsn_ie_len; + peer->cipher = cipher; + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M2 (expected %u)", lifetime, peer->lifetime); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + + /* Process MIC check to see if TPK M2 is right */ + if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + /* Discard the frame */ + wpa_tdls_del_key(sm, peer); + wpa_tdls_peer_free(sm, peer); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + wpa_tdls_set_key(sm, peer); + +skip_rsn: + peer->dtoken = dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " + "TPK Handshake Message 3"); + wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); + + wpa_tdls_enable_link(sm, peer); + + return 0; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; +} + + +static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + int ielen; + u16 status; + const u8 *pos; + u32 lifetime; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M3: " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); + + if (len < 3 + 3) + return -1; + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + status = WPA_GET_LE16(pos); + + if (status != 0) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", + status); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + pos += 2 /* status code */ + 1 /* dialog token */; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); + return -1; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", + (u8 *) kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); + return -1; + } + + if (!wpa_tdls_get_privacy(sm)) + goto skip_rsn; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", + kde.rsn_ie, kde.rsn_ie_len); + if (kde.rsn_ie_len != peer->rsnie_p_len || + os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " + "with the one sent in TPK M2"); + return -1; + } + + if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " + "not match with FTIE ANonce used in TPK M2"); + return -1; + } + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " + "match with FTIE SNonce used in TPK M1"); + return -1; + } + + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); + return -1; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", + (u8 *) timeoutie, sizeof(*timeoutie)); + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M3 (expected %u)", lifetime, peer->lifetime); + if (sm->tdls_external_setup) + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + return -1; + } + + if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + wpa_tdls_del_key(sm, peer); + wpa_tdls_peer_free(sm, peer); + return -1; + } + + if (wpa_tdls_set_key(sm, peer) < 0) + return -1; + +skip_rsn: + wpa_tdls_enable_link(sm, peer); + + return 0; +} + + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs) +{ + struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie; + + os_memset(lifetime, 0, ie_len); + lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL; + lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2; + lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(lifetime->value, tsecs); + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +/** + * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1) + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peer: MAC address of the peer STA + * Returns: 0 on success, or -1 on failure + * + * Send TPK Handshake Message 1 info to driver to start TDLS + * handshake with the peer. + */ +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + int tdls_prohibited = sm->tdls_prohibited; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + +#ifdef CONFIG_TDLS_TESTING + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - " + "reject request to start setup"); + return -1; + } + + /* Find existing entry and if found, use that instead of adding + * a new one */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + peer = wpa_tdls_add_peer(sm, addr); + if (peer == NULL) + return -1; + } + + peer->initiator = 1; + + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + + if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { + wpa_tdls_disable_link(sm, peer->addr); + return -1; + } + + return 0; +} + + +int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) + return -1; + + if (sm->tdls_external_setup) { + /* + * Disable previous link to allow renegotiation to be completed + * on AP path. + */ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + } + + return wpa_tdls_start(sm, addr); +} + + +/** + * wpa_supplicant_rx_tdls - Receive TDLS data frame + * + * This function is called to receive TDLS (ethertype = 0x890d) data frames. + */ +static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + struct wpa_tdls_frame *tf; + + wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation", + buf, len); + + if (sm->tdls_disabled || !sm->tdls_supported) { + wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled " + "or unsupported by driver"); + return; + } + + if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message"); + return; + } + + if (len < sizeof(*tf)) { + wpa_printf(MSG_INFO, "TDLS: Drop too short frame"); + return; + } + + /* Check to make sure its a valid encapsulated TDLS frame */ + tf = (struct wpa_tdls_frame *) buf; + if (tf->payloadtype != 2 /* TDLS_RFTYPE */ || + tf->category != WLAN_ACTION_TDLS) { + wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u " + "category=%u action=%u", + tf->payloadtype, tf->category, tf->action); + return; + } + + switch (tf->action) { + case WLAN_TDLS_SETUP_REQUEST: + wpa_tdls_process_tpk_m1(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + wpa_tdls_process_tpk_m2(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + wpa_tdls_process_tpk_m3(sm, src_addr, buf, len); + break; + case WLAN_TDLS_TEARDOWN: + wpa_tdls_recv_teardown(sm, src_addr, buf, len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + wpa_tdls_process_discovery_request(sm, src_addr, buf, len); + break; + default: + /* Kernel code will process remaining frames */ + wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u", + tf->action); + break; + } +} + + +/** + * wpa_tdls_init - Initialize driver interface parameters for TDLS + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters for TDLS. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_tdls_init(struct wpa_sm *sm) +{ + if (sm == NULL) + return -1; + + sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr, + ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, + sm, 0); + if (sm->l2_tdls == NULL) { + wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet " + "connection"); + return -1; + } + + /* + * Drivers that support TDLS but don't implement the get_capa callback + * are assumed to perform everything internally + */ + if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, + &sm->tdls_external_setup) < 0) { + sm->tdls_supported = 1; + sm->tdls_external_setup = 0; + } + + wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " + "driver", sm->tdls_supported ? "" : " not"); + wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", + sm->tdls_external_setup ? "external" : "internal"); + + return 0; +} + + +static void wpa_tdls_remove_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer, *tmp; + + peer = sm->tdls; + sm->tdls = NULL; + + while (peer) { + int res; + tmp = peer->next; + res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", + MAC2STR(peer->addr), res); + wpa_tdls_peer_free(sm, peer); + os_free(peer); + peer = tmp; + } +} + + +/** + * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS + * + * This function is called to recover driver interface parameters for TDLS + * and frees resources allocated for it. + */ +void wpa_tdls_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->l2_tdls) + l2_packet_deinit(sm->l2_tdls); + sm->l2_tdls = NULL; + + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_assoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association"); + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_disassoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation"); + wpa_tdls_remove_peers(sm); +} + + +static int wpa_tdls_prohibited(const u8 *ies, size_t len) +{ + struct wpa_eapol_ie_parse elems; + + if (ies == NULL) + return 0; + + if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) + return 0; + + if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return 0; + + /* bit 38 - TDLS Prohibited */ + return !!(elems.ext_capab[2 + 4] & 0x40); +} + + +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", + sm->tdls_prohibited ? "prohibited" : "allowed"); +} + + +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " + "(Re)Association Response IEs"); + sm->tdls_prohibited = 1; + } +} + + +void wpa_tdls_enable(struct wpa_sm *sm, int enabled) +{ + wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled"); + sm->tdls_disabled = !enabled; +} + + +int wpa_tdls_is_external_setup(struct wpa_sm *sm) +{ + return sm->tdls_external_setup; +} diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 9439f97..f35f9ee 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -17,6 +17,7 @@ #include "common.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" @@ -49,21 +50,26 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, * BSSID from the driver. */ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { - wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for " - "EAPOL-Key destination address"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); } else { dest = sm->bssid; - wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR - ") as the destination for EAPOL-Key", - MAC2STR(dest)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Use BSSID (" MACSTR + ") as the destination for EAPOL-Key", + MAC2STR(dest)); } } if (key_mic && wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { - wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key " - "version %d MIC", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key " + "version %d MIC", ver); goto out; } + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); @@ -97,8 +103,8 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { - wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " - "request"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); return; } @@ -124,9 +130,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_PUT_BE16(reply->key_data_length, 0); - wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " - "pairwise=%d ptk_set=%d len=%lu)", - error, pairwise, sm->ptk_set, (unsigned long) rlen); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Sending EAPOL-Key Request (error=%d " + "pairwise=%d ptk_set=%d len=%lu)", + error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? reply->key_mic : NULL); @@ -146,10 +153,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " - "PMKSA cache"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); } else { - wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); abort_cached = 1; } } @@ -190,26 +198,28 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; - if (sm->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_ft(sm->key_mgmt)) { pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid)) { - wpa_printf(MSG_DEBUG, "RSN: the new PMK " - "matches with the PMKID"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); abort_cached = 0; } } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " - "EAPOL state machines"); - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Key handshake aborted"); + "EAPOL state machines - key handshake " + "aborted"); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " - "caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { @@ -218,13 +228,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } } - if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) { + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; - wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger " - "full EAP authentication"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no PMKSA entry found - trigger " + "full EAP authentication"); buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, NULL, 0, &buflen, NULL); if (buf) { @@ -265,8 +277,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { - wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot " - "generate msg 2/4"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " + "cannot generate msg 2/4"); return -1; } @@ -321,6 +333,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, + WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); os_memcpy(reply + 1, wpa_ie, wpa_ie_len); @@ -328,7 +342,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -365,14 +379,14 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, int res; if (wpa_sm_get_network_ctx(sm) == NULL) { - wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " - "4)."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " + "found (msg 1 of 4)"); return; } wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); os_memset(&ie, 0, sizeof(ie)); @@ -382,7 +396,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, const u8 *_buf = (const u8 *) (key + 1); size_t len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); - wpa_supplicant_parse_ies(_buf, len, &ie); + if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) + goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); @@ -392,15 +407,15 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { - wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - " - "requesting full EAP authentication"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " + "msg 1/4 - requesting full EAP authentication"); return; } if (res) goto failed; if (sm->renew_snonce) { - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; @@ -462,15 +477,16 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * Start preauthentication after a short wait to avoid a * possible race condition between the data receive and key * configuration after the 4-Way Handshake. This increases the - * likelyhood of the first preauth EAPOL-Start frame getting to + * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { - wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted " - "opportunistic PMKSA entry - marking it valid"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Authenticator accepted " + "opportunistic PMKSA entry - marking it valid"); sm->cur_pmksa->opportunistic = 0; } @@ -486,7 +502,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; - wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } @@ -499,7 +515,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver."); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing PTK to the driver"); switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: @@ -513,12 +530,13 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, rsclen = 6; break; case WPA_CIPHER_NONE: - wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: " - "NONE - do not use pairwise keys"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " + "Suite: NONE - do not use pairwise keys"); return 0; default: - wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d", - sm->pairwise_cipher); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); return -1; } @@ -531,9 +549,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, (u8 *) sm->ptk.tk1, keylen) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " - "driver (alg=%d keylen=%d bssid=" MACSTR ")", - alg, keylen, MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set PTK to the " + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); return -1; } @@ -547,7 +566,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } -static int wpa_supplicant_check_group_cipher(int group_cipher, +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, int keylen, int maxkeylen, int *key_rsc_len, enum wpa_alg *alg) @@ -588,15 +608,16 @@ static int wpa_supplicant_check_group_cipher(int group_cipher, *alg = WPA_ALG_WEP; break; default: - wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", - group_cipher); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported Group Cipher %d", + group_cipher); return -1; } if (ret < 0 ) { - wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key " - "length %d (%d).", - wpa_cipher_txt(group_cipher), keylen, maxkeylen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported %s Group Cipher key length %d (%d)", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); } return ret; @@ -619,9 +640,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, u8 gtk_buf[32]; wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); - wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " - "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx, - gd->gtk_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", + gd->keyidx, gd->tx, gd->gtk_len); wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); if (sm->group_cipher == WPA_CIPHER_TKIP) { /* Swap Tx/Rx keys for Michael MIC */ @@ -631,21 +652,21 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, _gtk = gtk_buf; } if (sm->pairwise_cipher == WPA_CIPHER_NONE) { - if (wpa_sm_set_key(sm, gd->alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", + if (wpa_sm_set_key(sm, gd->alg, NULL, gd->keyidx, 1, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set " - "GTK to the driver (Group only)."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to the driver " + "(Group only)"); return -1; } - } else if (wpa_sm_set_key(sm, gd->alg, - (u8 *) "\xff\xff\xff\xff\xff\xff", + } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " - "the driver (alg=%d keylen=%d keyidx=%d)", - gd->alg, gd->gtk_len, gd->keyidx); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to " + "the driver (alg=%d keylen=%d keyidx=%d)", + gd->alg, gd->gtk_len, gd->keyidx); return -1; } @@ -662,8 +683,9 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, * doing Group Key only APs) and without this workaround, the * data connection does not work because wpa_supplicant * configured non-zero keyidx to be used for unicast. */ - wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise " - "keys are used - ignore Tx bit"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); return 0; } return tx; @@ -702,11 +724,12 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Failed to install GTK"); return -1; } @@ -733,22 +756,21 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, return -1; igtk = (const struct wpa_igtk_kde *) ie->igtk; keyidx = WPA_GET_LE16(igtk->keyid); - wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d " - "pn %02x%02x%02x%02x%02x%02x", - keyidx, MAC2STR(igtk->pn)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d " + "pn %02x%02x%02x%02x%02x%02x", + keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, WPA_IGTK_LEN); if (keyidx > 4095) { - wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d", - keyidx); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); return -1; } - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, - (u8 *) "\xff\xff\xff\xff\xff\xff", + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK" - " to the driver"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); return -1; } } @@ -774,8 +796,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (wpa_ie) { if (!sm->ap_wpa_ie) { - wpa_printf(MSG_INFO, "WPA: No WPA IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No WPA IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", wpa_ie, wpa_ie_len); @@ -787,8 +809,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (rsn_ie) { if (!sm->ap_rsn_ie) { - wpa_printf(MSG_INFO, "WPA: No RSN IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No RSN IE in Beacon/ProbeResp"); } wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", rsn_ie, rsn_ie_len); @@ -811,15 +833,15 @@ static int ft_validate_mdie(struct wpa_sm *sm, if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not " - "match with the current mobility domain"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " + "not match with the current mobility domain"); return -1; } if (assoc_resp_mdie && (assoc_resp_mdie[1] != ie->mdie[1] || os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { - wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", ie->mdie, 2 + ie->mdie[1]); wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", @@ -837,7 +859,8 @@ static int ft_validate_ftie(struct wpa_sm *sm, const u8 *assoc_resp_ftie) { if (ie->ftie == NULL) { - wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: No FTIE in EAPOL-Key msg 3/4"); return -1; } @@ -846,7 +869,7 @@ static int ft_validate_ftie(struct wpa_sm *sm, if (assoc_resp_ftie[1] != ie->ftie[1] || os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { - wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", ie->ftie, 2 + ie->ftie[1]); wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", @@ -873,14 +896,15 @@ static int ft_validate_rsnie(struct wpa_sm *sm, */ if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || rsn.num_pmkid != 1 || rsn.pmkid == NULL) { - wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " - "FT 4-way handshake message 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 3/4"); return -1; } if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in " - "FT 4-way handshake message 3/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: PMKR1Name mismatch in " + "FT 4-way handshake message 3/4"); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", rsn.pmkid, WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", @@ -932,14 +956,17 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) { - wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " - "Trying to get from scan results"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: No WPA/RSN IE for this AP known. " + "Trying to get from scan results"); if (wpa_sm_get_beacon_ie(sm) < 0) { - wpa_printf(MSG_WARNING, "WPA: Could not find AP from " - "the scan results"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not find AP from " + "the scan results"); } else { - wpa_printf(MSG_DEBUG, "WPA: Found the current AP from " - "updated scan results"); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Found the current AP from " + "updated scan results"); } } @@ -1034,7 +1061,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, if (kde) os_memcpy(reply + 1, kde, kde_len); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -1051,29 +1078,32 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); - wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver); key_info = WPA_GET_BE16(key->key_info); pos = (const u8 *) (key + 1); len = WPA_GET_BE16(key->key_data_length); wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); - wpa_supplicant_parse_ies(pos, len, &ie); + if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key " - "data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: IGTK KDE in unencrypted key data"); goto failed; } if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { - wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", - (unsigned long) ie.igtk_len); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ @@ -1082,9 +1112,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " - "Handshake differs from 3 of 4-Way Handshake - drop" - " packet (src=" MACSTR ")", MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: ANonce from message 1 of 4-Way Handshake " + "differs from 3 of 4-Way Handshake - drop packet (src=" + MACSTR ")", MAC2STR(sm->bssid)); goto failed; } @@ -1092,17 +1123,17 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, switch (sm->pairwise_cipher) { case WPA_CIPHER_CCMP: if (keylen != 16) { - wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " - "%d (src=" MACSTR ")", - keylen, MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid CCMP key length %d (src=" MACSTR + ")", keylen, MAC2STR(sm->bssid)); goto failed; } break; case WPA_CIPHER_TKIP: if (keylen != 32) { - wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " - "%d (src=" MACSTR ")", - keylen, MAC2STR(sm->bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid TKIP key length %d (src=" MACSTR + ")", keylen, MAC2STR(sm->bssid)); goto failed; } break; @@ -1134,15 +1165,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure GTK"); goto failed; } if (ieee80211w_set_keys(sm, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); goto failed; } + wpa_sm_set_rekey_offload(sm); + return; failed: @@ -1160,18 +1195,21 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); - wpa_supplicant_parse_ies(keydata, keydatalen, &ie); + if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); return -1; } if (ie.gtk == NULL) { - wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No GTK IE in Group Key msg 1/2"); return -1; } maxkeylen = gd->gtk_len = ie.gtk_len - 2; - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1182,14 +1220,16 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, !!(ie.gtk[0] & BIT(2))); if (ie.gtk_len - 2 > sizeof(gd->gtk)) { - wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " - "(len=%lu)", (unsigned long) ie.gtk_len - 2); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Too long GTK in GTK IE (len=%lu)", + (unsigned long) ie.gtk_len - 2); return -1; } os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2); if (ieee80211w_set_keys(sm, &ie) < 0) - wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); return 0; } @@ -1207,22 +1247,23 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, gd->gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = keydatalen; if (keydatalen > extra_len) { - wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" - " key_data_length=%lu > extra_len=%lu", - (unsigned long) keydatalen, - (unsigned long) extra_len); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Truncated EAPOL-Key packet: " + "key_data_length=%lu > extra_len=%lu", + (unsigned long) keydatalen, (unsigned long) extra_len); return -1; } if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { - wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)", - (unsigned long) maxkeylen); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1233,38 +1274,42 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (keydatalen > sizeof(gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: RC4 key data " - "too long (%lu)", - (unsigned long) keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 key data too long (%lu)", + (unsigned long) keydatalen); return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { - wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (keydatalen % 8) { - wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP " - "len %lu", (unsigned long) keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %lu", + (unsigned long) keydatalen); return -1; } if (maxkeylen > sizeof(gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data " - "too long (keydatalen=%lu maxkeylen=%lu)", - (unsigned long) keydatalen, - (unsigned long) maxkeylen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES-WRAP key data " + "too long (keydatalen=%lu maxkeylen=%lu)", + (unsigned long) keydatalen, + (unsigned long) maxkeylen); return -1; } if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, (const u8 *) (key + 1), gd->gtk)) { - wpa_printf(MSG_WARNING, "WPA: AES unwrap " - "failed - could not decrypt GTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - could not decrypt " + "GTK"); return -1; } } else { - wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", - ver); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( @@ -1300,7 +1345,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_PUT_BE16(reply->key_data_length, 0); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL, rbuf, rlen, reply->key_mic); @@ -1320,8 +1365,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, os_memset(&gd, 0, sizeof(gd)); rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; - wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from " - MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); keydatalen = WPA_GET_BE16(key->key_data_length); @@ -1352,6 +1397,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); + + wpa_sm_set_rekey_offload(sm); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & @@ -1378,8 +1425,9 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { - wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " - "when using TPTK - ignoring TPTK"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); } else { ok = 1; sm->tptk_set = 0; @@ -1393,16 +1441,18 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, key->key_mic); if (os_memcmp(mic, key->key_mic, 16) != 0) { - wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " - "- dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC - " + "dropping packet"); return -1; } ok = 1; } if (!ok) { - wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC " - "- dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not verify EAPOL-Key MIC - " + "dropping packet"); return -1; } @@ -1422,8 +1472,9 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", (u8 *) (key + 1), keydatalen); if (!sm->ptk_set) { - wpa_printf(MSG_WARNING, "WPA: PTK not available, " - "cannot decrypt EAPOL-Key key data."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: PTK not available, cannot decrypt EAPOL-Key Key " + "Data"); return -1; } @@ -1434,37 +1485,40 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { - wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); return -1; } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { u8 *buf; if (keydatalen % 8) { - wpa_printf(MSG_WARNING, "WPA: Unsupported " - "AES-WRAP len %d", keydatalen); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %d", + keydatalen); return -1; } keydatalen -= 8; /* AES-WRAP adds 8 bytes */ buf = os_malloc(keydatalen); if (buf == NULL) { - wpa_printf(MSG_WARNING, "WPA: No memory for " - "AES-UNWRAP buffer"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: No memory for AES-UNWRAP buffer"); return -1; } if (aes_unwrap(sm->ptk.kek, keydatalen / 8, (u8 *) (key + 1), buf)) { os_free(buf); - wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - " - "could not decrypt EAPOL-Key key data"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); return -1; } os_memcpy(key + 1, buf, keydatalen); os_free(buf); WPA_PUT_BE16(key->key_data_length, keydatalen); } else { - wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", - ver); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", @@ -1480,35 +1534,38 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } -static void wpa_eapol_key_dump(const struct wpa_eapol_key *key) +static void wpa_eapol_key_dump(struct wpa_sm *sm, + const struct wpa_eapol_key *key) { #ifndef CONFIG_NO_STDOUT_DEBUG u16 key_info = WPA_GET_BE16(key->key_info); - wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type); - wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s" - "%s%s%s%s%s%s%s)", - key_info, key_info & WPA_KEY_INFO_TYPE_MASK, - (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> - WPA_KEY_INFO_KEY_INDEX_SHIFT, - (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, - key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", - key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", - key_info & WPA_KEY_INFO_ACK ? " Ack" : "", - key_info & WPA_KEY_INFO_MIC ? " MIC" : "", - key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", - key_info & WPA_KEY_INFO_ERROR ? " Error" : "", - key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", - key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); - wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u", - WPA_GET_BE16(key->key_length), - WPA_GET_BE16(key->key_data_length)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)", + key_info, key_info & WPA_KEY_INFO_TYPE_MASK, + (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT, + (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13, + key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group", + key_info & WPA_KEY_INFO_INSTALL ? " Install" : "", + key_info & WPA_KEY_INFO_ACK ? " Ack" : "", + key_info & WPA_KEY_INFO_MIC ? " MIC" : "", + key_info & WPA_KEY_INFO_SECURE ? " Secure" : "", + key_info & WPA_KEY_INFO_ERROR ? " Error" : "", + key_info & WPA_KEY_INFO_REQUEST ? " Request" : "", + key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : ""); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + " key_length=%u key_data_length=%u", + WPA_GET_BE16(key->key_length), + WPA_GET_BE16(key->key_data_length)); wpa_hexdump(MSG_DEBUG, " replay_counter", key->replay_counter, WPA_REPLAY_COUNTER_LEN); wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN); @@ -1552,10 +1609,11 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #endif /* CONFIG_IEEE80211R */ if (len < sizeof(*hdr) + sizeof(*key)) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " - "EAPOL-Key (len %lu, expecting at least %lu)", - (unsigned long) len, - (unsigned long) sizeof(*hdr) + sizeof(*key)); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame too short to be a WPA " + "EAPOL-Key (len %lu, expecting at least %lu)", + (unsigned long) len, + (unsigned long) sizeof(*hdr) + sizeof(*key)); return 0; } @@ -1568,40 +1626,45 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, key = (struct wpa_eapol_key *) (hdr + 1); plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); - wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", - hdr->version, hdr->type, (unsigned long) plen); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "IEEE 802.1X RX: version=%d type=%d length=%lu", + hdr->version, hdr->type, (unsigned long) plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame (type %u) discarded, " "not a Key frame", hdr->type); ret = 0; goto out; } if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " - "invalid (frame size %lu)", - (unsigned long) plen, (unsigned long) len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); ret = 0; goto out; } if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { - wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " - "discarded", key->type); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL-Key type (%d) unknown, discarded", + key->type); ret = 0; goto out; } - wpa_eapol_key_dump(key); + wpa_eapol_key_dump(sm, key); eapol_sm_notify_lower_layer_success(sm->eapol, 0); wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); if (data_len < len) { - wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " - "802.1X data", (unsigned long) len - data_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: ignoring %lu bytes after the IEEE 802.1X data", + (unsigned long) len - data_len); } key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; @@ -1610,8 +1673,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " - "version %d.", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); goto out; } @@ -1619,8 +1683,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { - wpa_printf(MSG_INFO, "FT: AP did not use " - "AES-128-CMAC."); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FT: AP did not use AES-128-CMAC"); goto out; } } else @@ -1628,25 +1692,27 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { - wpa_printf(MSG_INFO, "WPA: AP did not use the " - "negotiated AES-128-CMAC."); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: AP did not use the " + "negotiated AES-128-CMAC"); goto out; } } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " - "descriptor version (%d) is not 2.", ver); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: CCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); if (sm->group_cipher != WPA_CIPHER_CCMP && !(key_info & WPA_KEY_INFO_KEY_TYPE)) { /* Earlier versions of IEEE 802.11i did not explicitly * require version 2 descriptor for all EAPOL-Key * packets, so allow group keys to use version 1 if * CCMP is not used for them. */ - wpa_printf(MSG_INFO, "WPA: Backwards compatibility: " - "allow invalid version for non-CCMP group " - "keys"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Backwards compatibility: allow invalid " + "version for non-CCMP group keys"); } else goto out; } @@ -1661,9 +1727,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (!peerkey->initiator && peerkey->replay_counter_set && os_memcmp(key->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay " - "Counter did not increase (STK) - dropping " - "packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: EAPOL-Key Replay Counter did not " + "increase (STK) - dropping packet"); goto out; } else if (peerkey->initiator) { u8 _tmp[WPA_REPLAY_COUNTER_LEN]; @@ -1672,16 +1738,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); if (os_memcmp(_tmp, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN) != 0) { - wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay " - "Counter did not match (STK) - " - "dropping packet"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: EAPOL-Key Replay " + "Counter did not match (STK) - " + "dropping packet"); goto out; } } } if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { - wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Ack bit in key_info from STK peer"); goto out; } #endif /* CONFIG_PEERKEY */ @@ -1689,8 +1757,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (!peerkey && sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not" - " increase - dropping packet"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key Replay Counter did not increase - " + "dropping packet"); goto out; } @@ -1699,13 +1768,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, && (peerkey == NULL || !peerkey->initiator) #endif /* CONFIG_PEERKEY */ ) { - wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No Ack bit in key_info"); goto out; } if (key_info & WPA_KEY_INFO_REQUEST) { - wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - " - "dropped"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: EAPOL-Key with Request bit - dropped"); goto out; } @@ -1739,8 +1809,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, if (key_info & WPA_KEY_INFO_KEY_TYPE) { if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { - wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key " - "(Pairwise) with non-zero key index"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Ignored EAPOL-Key (Pairwise) with " + "non-zero key index"); goto out; } if (peerkey) { @@ -1764,8 +1835,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, wpa_supplicant_process_1_of_2(sm, src_addr, key, extra_len, ver); } else { - wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) " - "without Mic bit - dropped"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); } } @@ -1940,7 +2012,8 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, if (sm->cur_pmksa == entry || (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { - wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: removed current PMKSA entry"); sm->cur_pmksa = NULL; if (replace) { @@ -1982,8 +2055,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); if (sm->pmksa == NULL) { - wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization " - "failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } @@ -2030,7 +2103,8 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) if (sm == NULL) return; - wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Association event - clear replay counter"); os_memcpy(sm->bssid, bssid, ETH_ALEN); os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->rx_replay_counter_set = 0; @@ -2059,10 +2133,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * this is not part of a Fast BSS Transition. */ - wpa_printf(MSG_DEBUG, "WPA: Clear old PTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; sm->tptk_set = 0; } + +#ifdef CONFIG_TDLS + wpa_tdls_assoc(sm); +#endif /* CONFIG_TDLS */ } @@ -2078,6 +2156,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) rsn_preauth_deinit(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; +#ifdef CONFIG_TDLS + wpa_tdls_disassoc(sm); +#endif /* CONFIG_TDLS */ } @@ -2191,8 +2272,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; } - if (config == NULL || config->network_ctx != sm->network_ctx) - pmksa_cache_notify_reconfig(sm->pmksa); } @@ -2430,7 +2509,8 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { @@ -2464,7 +2544,8 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { @@ -2498,7 +2579,8 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { @@ -2526,9 +2608,12 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) */ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) { - if (sm == NULL || sm->assoc_wpa_ie == NULL) { - wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from " - "association info"); + if (sm == NULL) + return -1; + + if (sm->assoc_wpa_ie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: No WPA/RSN IE available from association info"); return -1; } if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) @@ -2549,7 +2634,7 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) void wpa_sm_drop_sa(struct wpa_sm *sm) { - wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); @@ -2564,3 +2649,17 @@ int wpa_sm_has_ptk(struct wpa_sm *sm) return 0; return sm->ptk_set; } + + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) +{ + os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); +} + + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ +#ifndef CONFIG_NO_WPA2 + pmksa_cache_flush(sm->pmksa, network_ctx); +#endif /* CONFIG_NO_WPA2 */ +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index f1a5554..4c1750f 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -55,6 +55,19 @@ struct wpa_sm_ctx { int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); int (*mark_authenticated)(void *ctx, const u8 *target_ap); +#ifdef CONFIG_TDLS + int (*tdls_get_capa)(void *ctx, int *tdls_supported, + int *tdls_ext_setup); + int (*send_tdls_mgmt)(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(void *ctx, int oper, const u8 *peer); + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); }; @@ -126,6 +139,10 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); void wpa_sm_drop_sa(struct wpa_sm *sm); int wpa_sm_has_ptk(struct wpa_sm *sm); +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); + #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) @@ -271,6 +288,16 @@ static inline int wpa_sm_has_ptk(struct wpa_sm *sm) return 0; } +static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, + const u8 *replay_ctr) +{ +} + +static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY @@ -330,4 +357,19 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ + +/* tdls.c */ +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_deinit(struct wpa_sm *sm); +void wpa_tdls_enable(struct wpa_sm *sm, int enabled); +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_is_external_setup(struct wpa_sm *sm); + #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 23063bc..f6fd7da 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -16,6 +16,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wpa.h" @@ -24,31 +25,6 @@ #ifdef CONFIG_IEEE80211R -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *tie; - size_t tie_len; - const u8 *igtk; - size_t igtk_len; - const u8 *ric; - size_t ric_len; -}; - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse); - - int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len) @@ -346,155 +322,6 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, } -static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - - parse->ftie = ie; - parse->ftie_len = ie_len; - - pos = ie + sizeof(struct rsn_ftie); - end = ie + ie_len; - - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r1kh_id = pos + 2; - break; - case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; - break; - case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; - break; -#ifdef CONFIG_IEEE80211W - case FTIE_SUBELEM_IGTK: - parse->igtk = pos + 2; - parse->igtk_len = pos[1]; - break; -#endif /* CONFIG_IEEE80211W */ - } - - pos += 2 + pos[1]; - } - - return 0; -} - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - struct wpa_ie_data data; - int ret; - const struct rsn_ftie *ftie; - int prot_ie_count = 0; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) - return -1; - ftie = (const struct rsn_ftie *) (pos + 2); - prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - parse->tie = pos + 2; - parse->tie_len = pos[1]; - break; - case WLAN_EID_RIC_DATA: - if (parse->ric == NULL) - parse->ric = pos; - } - - pos += 2 + pos[1]; - } - - if (prot_ie_count == 0) - return 0; /* no MIC */ - - /* - * Check that the protected IE count matches with IEs included in the - * frame. - */ - if (parse->rsn) - prot_ie_count--; - if (parse->mdie) - prot_ie_count--; - if (parse->ftie) - prot_ie_count--; - if (parse->tie) - prot_ie_count--; - if (prot_ie_count < 0) { - wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " - "the protected IE count"); - return -1; - } - - if (prot_ie_count == 0 && parse->ric) { - wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " - "included in protected IE count"); - return -1; - } - - /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; - } - parse->ric_len = pos - parse->ric; - if (prot_ie_count) { - wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " - "frame", (int) prot_ie_count); - return -1; - } - - return 0; -} - - static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; @@ -540,7 +367,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) size_t ft_ies_len; /* Generate a new SNonce */ - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } @@ -795,9 +622,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); - if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < - 0) { + if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, + gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); return -1; @@ -848,9 +674,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < - 0) { + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, + igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); return -1; @@ -951,8 +776,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, } count = 3; - if (parse.tie) - count++; + if (parse.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", @@ -1020,7 +845,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, MAC2STR(target_ap)); /* Generate a new SNonce */ - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 618c090..39124c4 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -18,6 +18,7 @@ #include "utils/list.h" struct wpa_peerkey; +struct wpa_tdls_peer; struct wpa_eapol_key; /** @@ -43,6 +44,7 @@ struct wpa_sm { struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; + struct l2_packet_data *l2_tdls; u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or * 00:00:00:00:00:00 if no pre-auth is * in progress */ @@ -92,6 +94,20 @@ struct wpa_sm { #ifdef CONFIG_PEERKEY struct wpa_peerkey *peerkey; #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + struct wpa_tdls_peer *tdls; + int tdls_prohibited; + int tdls_disabled; + + /* The driver supports TDLS */ + int tdls_supported; + + /* + * The driver requires explicit discovery/setup/teardown frames sent + * to it via tdls_mgmt. + */ + int tdls_external_setup; +#endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ @@ -237,6 +253,57 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, return -1; } +static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) +{ + if (!sm->ctx->set_rekey_offload) + return; + sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, + sm->ptk.kck, sm->rx_replay_counter); +} + +#ifdef CONFIG_TDLS +static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, + int *tdls_supported, + int *tdls_ext_setup) +{ + if (sm->ctx->tdls_get_capa) + return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, + tdls_ext_setup); + return -1; +} + +static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, + size_t len) +{ + if (sm->ctx->send_tdls_mgmt) + return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, + dialog_token, status_code, + buf, len); + return -1; +} + +static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, + const u8 *peer) +{ + if (sm->ctx->tdls_oper) + return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); + return -1; +} + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + capability, supp_rates, + supp_rates_len); + return -1; +} +#endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, @@ -256,4 +323,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len); +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + #endif /* WPA_I_H */ diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index f447223..cbbc54f 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -22,144 +22,6 @@ #include "wpa_ie.h" -static int wpa_selector_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) - return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) - return WPA_CIPHER_WEP40; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) - return WPA_CIPHER_TKIP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) - return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(const u8 *s) -{ - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) - return WPA_KEY_MGMT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) - return WPA_KEY_MGMT_PSK; - if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) - return WPA_KEY_MGMT_WPA_NONE; - return 0; -} - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - const struct wpa_ie_hdr *hdr; - const u8 *pos; - int left; - int i, count; - - os_memset(data, 0, sizeof(*data)); - data->proto = WPA_PROTO_WPA; - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - data->capabilities = 0; - data->pmkid = NULL; - data->num_pmkid = 0; - data->mgmt_group_cipher = 0; - - if (wpa_ie_len == 0) { - /* No WPA IE - fail silently */ - return -1; - } - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { - wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", - __func__, (unsigned long) wpa_ie_len); - return -1; - } - - hdr = (const struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || - hdr->len != wpa_ie_len - 2 || - RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || - WPA_GET_LE16(hdr->version) != WPA_VERSION) { - wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", - __func__); - return -1; - } - - pos = (const u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", - __func__, left); - return -1; - } - - if (left >= 2) { - data->pairwise_cipher = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { - wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " - "count %u left %u", __func__, count, left); - return -1; - } - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) { - wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", - __func__); - return -1; - } - - if (left >= 2) { - data->key_mgmt = 0; - count = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { - wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " - "count %u left %u", __func__, count, left); - return -1; - } - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) { - wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", - __func__); - return -1; - } - - if (left >= 2) { - data->capabilities = WPA_GET_LE16(pos); - pos += 2; - left -= 2; - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", - __func__, left); - } - - return 0; -} - - /** * wpa_parse_wpa_ie - Parse WPA/RSN IE * @wpa_ie: Pointer to WPA or RSN IE @@ -535,7 +397,6 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); -#ifdef CONFIG_IEEE80211R } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; @@ -562,7 +423,20 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, "EAPOL-Key Key Data IE", pos, 2 + pos[1]); } -#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_LINK_ID) { + if (pos[1] >= 18) { + ie->lnkid = pos; + ie->lnkid_len = pos[1] + 2; + } + } else if (*pos == WLAN_EID_EXT_CAPAB) { + ie->ext_capab = pos; + ie->ext_capab_len = pos[1] + 2; + } else if (*pos == WLAN_EID_SUPP_RATES) { + ie->supp_rates = pos; + ie->supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { + ie->ext_supp_rates = pos; + ie->ext_supp_rates_len = pos[1] + 2; } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 94518d8..c13d94c 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -15,6 +15,8 @@ #ifndef WPA_IE_H #define WPA_IE_H +struct wpa_sm; + struct wpa_eapol_ie_parse { const u8 *wpa_ie; size_t wpa_ie_len; @@ -39,14 +41,20 @@ struct wpa_eapol_ie_parse { const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; const u8 *reassoc_deadline; const u8 *key_lifetime; -#endif /* CONFIG_IEEE80211R */ + const u8 *lnkid; + size_t lnkid_len; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_rates; + size_t supp_rates_len; + const u8 *ext_supp_rates; + size_t ext_supp_rates_len; }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 1374264..1c02167 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -572,7 +572,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c) /* this is a shell function that calls either the normal or Montgomery * exptmod functions. Originally the call to the montgomery code was - * embedded in the normal function but that wasted alot of stack space + * embedded in the normal function but that wasted a lot of stack space * for nothing (since 99% of the time the Montgomery code would be called) */ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) @@ -2207,7 +2207,7 @@ static int mp_2expt (mp_int * a, int b) /* zero a as per default */ mp_zero (a); - /* grow a to accomodate the single bit */ + /* grow a to accommodate the single bit */ if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { return res; } @@ -2319,7 +2319,7 @@ CLEANUP: } -/* multiplies |a| * |b| and only computes upto digs digits of result +/* multiplies |a| * |b| and only computes up to digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ @@ -2829,7 +2829,7 @@ static int mp_mul_2(mp_int * a, mp_int * b) { int x, res, oldused; - /* grow to accomodate result */ + /* grow to accommodate result */ if (b->alloc < a->used + 1) { if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { return res; @@ -2891,8 +2891,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* * shifts with subtractions when the result is greater than b. * - * The method is slightly modified to shift B unconditionally upto just under - * the leading bit of b. This saves alot of multiple precision shifting. + * The method is slightly modified to shift B unconditionally up to just under + * the leading bit of b. This saves a lot of multiple precision shifting. */ static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) { diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index afb6031..75b8612 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -80,8 +80,9 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); - key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + - conn->rl.iv_size); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { @@ -107,12 +108,21 @@ int tls_derive_keys(struct tlsv1_client *conn, os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); pos += conn->rl.key_material_len; - /* client_write_IV */ - os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; - /* server_write_IV */ - os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; + if (conn->rl.tls_version == TLS_VERSION_1) { + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } return 0; } @@ -227,10 +237,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); - os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, - out_data, out_len, in_len, &rlen) < 0) { + out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -360,6 +368,8 @@ struct tlsv1_client * tlsv1_client_init(void) suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; + conn->rl.tls_version = TLS_VERSION; + return conn; } @@ -656,6 +666,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +{ + conn->disable_time_checks = !enabled; +} + + void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx) diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 16ad57d..a620d62 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -1,6 +1,6 @@ /* * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -47,6 +47,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h index 7fe179f..f091bcf 100644 --- a/src/tls/tlsv1_client_i.h +++ b/src/tls/tlsv1_client_i.h @@ -1,6 +1,6 @@ /* * TLSv1 client - internal structures - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -39,6 +39,7 @@ struct tlsv1_client { unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; struct crypto_public_key *server_rsa_key; diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index ed3f260..457c3b0 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* - * TLSv1 client - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - read handshake message + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -38,6 +38,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; + u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " @@ -79,15 +80,22 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; - if (WPA_GET_BE16(pos) != TLS_VERSION) { + tls_version = WPA_GET_BE16(pos); + if (tls_version != TLS_VERSION_1 && + (tls_version != TLS_VERSION_1_1 || + TLS_VERSION == TLS_VERSION_1)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ServerHello"); + "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + conn->rl.tls_version = tls_version; + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -365,7 +373,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, if (conn->cred && x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, conn->disable_time_checks) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index b47425f..9d53dd1 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* - * TLSv1 client - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - write handshake message + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -18,6 +18,7 @@ #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -57,7 +58,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) os_get_time(&now); WPA_PUT_BE32(conn->client_random, now.sec); - if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "client_random"); return NULL; @@ -115,7 +116,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, out_len) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + out_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -191,7 +193,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -222,7 +225,7 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(csecret, csecret_len)) { + if (random_get_bytes(csecret, csecret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -412,7 +415,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -533,7 +537,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -552,17 +557,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -577,7 +581,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -586,13 +590,11 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; - pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ @@ -621,40 +623,35 @@ static int tls_write_client_finished(struct tlsv1_client *conn, if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, - verify_data, TLS_VERIFY_DATA_LEN)) { + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", - verify_data, TLS_VERIFY_DATA_LEN); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; - os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); - pos += TLS_VERIFY_DATA_LEN; - WPA_PUT_BE24(hs_length, pos - hs_length - 3); + pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -668,7 +665,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, *out_len = 0; - msglen = 1000; + msglen = 2000; if (conn->certificate_requested) msglen += tls_client_cert_chain_der_len(conn); @@ -777,7 +774,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 763a4af..712d276 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -1,6 +1,6 @@ /* - * TLSv1 common definitions - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) common definitions + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -17,7 +17,13 @@ #include "crypto/crypto.h" -#define TLS_VERSION 0x0301 /* TLSv1 */ +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index aa467ef..3e07245 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -46,7 +46,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) static int tlsv1_add_cert_der(struct x509_certificate **chain, const u8 *buf, size_t len) { - struct x509_certificate *cert; + struct x509_certificate *cert, *p; char name[128]; cert = x509_certificate_parse(buf, len); @@ -56,8 +56,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain, return -1; } - cert->next = *chain; - *chain = cert; + p = *chain; + while (p && p->next) + p = p->next; + if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { + /* + * The new certificate is the issuer of the last certificate in + * the chain - add the new certificate to the end. + */ + p->next = cert; + } else { + /* Add to the beginning of the chain */ + cert->next = *chain; + *chain = cert; + } x509_name_string(&cert->subject, name, sizeof(name)); wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c index e811f0e..e072324 100644 --- a/src/tls/tlsv1_record.c +++ b/src/tls/tlsv1_record.c @@ -1,6 +1,6 @@ /* - * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -138,10 +138,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * tlsv1_record_send - TLS record layer: Send a message * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) - * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the - * beginning for record layer to fill in; payload filled in after this and - * extra space in the end for HMAC). + * @buf: Buffer for the generated TLS message (needs to have extra space for + * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size + * @payload: Payload to be sent * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure @@ -150,29 +150,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * the data using the current write cipher. */ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, - size_t buf_size, size_t payload_len, size_t *out_len) + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len) { - u8 *pos, *ct_start, *length, *payload; + u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; + int explicit_iv; pos = buf; + if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) + return -1; + /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; - /* opaque fragment[TLSPlaintext.length] */ - payload = pos; + cpayload = pos; + explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && + rl->iv_size && rl->tls_version == TLS_VERSION_1_1; + if (explicit_iv) { + /* opaque IV[Cipherspec.block_length] */ + if (pos + rl->iv_size > buf + buf_size) + return -1; + + /* + * Use random number R per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + + if (os_get_random(pos, rl->iv_size)) + return -1; + pos += rl->iv_size; + } + + /* + * opaque fragment[TLSPlaintext.length] + * (opaque content[TLSCompressed.length] in GenericBlockCipher) + */ + if (pos + payload_len > buf + buf_size) + return -1; + os_memmove(pos, payload, payload_len); pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + /* + * MAC calculated over seq_num + TLSCompressed.type + + * TLSCompressed.version + TLSCompressed.length + + * TLSCompressed.fragment + */ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { @@ -182,7 +215,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, } crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ - crypto_hash_update(hmac, ct_start, pos - ct_start); + crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); + crypto_hash_update(hmac, payload, payload_len); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " @@ -200,7 +234,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos, clen); pos += clen; if (rl->iv_size) { - size_t len = pos - payload; + size_t len = pos - cpayload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) @@ -214,8 +248,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos += pad + 1; } - if (crypto_cipher_encrypt(rl->write_cbc, payload, - payload, pos - payload) < 0) + if (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) return -1; } @@ -250,6 +284,7 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; + int force_mac_error = 0; wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", in_data, in_len); @@ -275,9 +310,14 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, return -1; } - if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) { + /* + * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the + * protocol version in record layer. As such, accept any {03,xx} value + * to remain compatible with existing implementations. + */ + if (in_data[1] != 0x03) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " - "%d.%d", in_data[1], in_data[2]); + "%u.%u", in_data[1], in_data[2]); *alert = TLS_ALERT_PROTOCOL_VERSION; return -1; } @@ -312,58 +352,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, return -1; } - os_memcpy(out_data, in_data, in_len); - *out_len = in_len; - if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { - if (crypto_cipher_decrypt(rl->read_cbc, out_data, + size_t plen; + if (crypto_cipher_decrypt(rl->read_cbc, in_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } + plen = in_len; + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data", out_data, plen); + if (rl->iv_size) { - if (in_len == 0) { + /* + * TLS v1.0 defines different alert values for various + * failures. That may information to aid in attacks, so + * use the same bad_record_mac alert regardless of the + * issues. + * + * In addition, instead of returning immediately on + * error, run through the MAC check to make timing + * attacks more difficult. + */ + + if (rl->tls_version == TLS_VERSION_1_1) { + /* Remove opaque IV[Cipherspec.block_length] */ + if (plen < rl->iv_size) { + wpa_printf(MSG_DEBUG, "TLSv1.1: Not " + "enough room for IV"); + force_mac_error = 1; + goto check_mac; + } + os_memmove(out_data, out_data + rl->iv_size, + plen - rl->iv_size); + plen -= rl->iv_size; + } + + /* Verify and remove padding */ + if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + force_mac_error = 1; + goto check_mac; } - padlen = out_data[in_len - 1]; - if (padlen >= in_len) { + padlen = out_data[plen - 1]; + if (padlen >= plen) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " - "length (%u, in_len=%lu) in " + "length (%u, plen=%lu) in " "received record", - padlen, (unsigned long) in_len); - *alert = TLS_ALERT_DECRYPTION_FAILED; - return -1; + padlen, (unsigned long) plen); + force_mac_error = 1; + goto check_mac; } - for (i = in_len - padlen; i < in_len; i++) { + for (i = plen - padlen - 1; i < plen - 1; i++) { if (out_data[i] != padlen) { wpa_hexdump(MSG_DEBUG, "TLSv1: Invalid pad in " "received record", - out_data + in_len - padlen, - padlen); - *alert = TLS_ALERT_DECRYPTION_FAILED; - return -1; + out_data + plen - padlen - + 1, padlen + 1); + force_mac_error = 1; + goto check_mac; } } - *out_len -= padlen + 1; + plen -= padlen + 1; } - wpa_hexdump(MSG_MSGDUMP, - "TLSv1: Record Layer - Decrypted data", - out_data, in_len); + check_mac: + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data with IV and padding removed", + out_data, plen); - if (*out_len < rl->hash_size) { + if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); - *alert = TLS_ALERT_INTERNAL_ERROR; + *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } - *out_len -= rl->hash_size; + plen -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); @@ -377,22 +445,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); - WPA_PUT_BE16(len, *out_len); + WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); - crypto_hash_update(hmac, out_data, *out_len); + crypto_hash_update(hmac, out_data, plen); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (hlen != rl->hash_size || - os_memcmp(hash, out_data + *out_len, hlen) != 0) { + os_memcmp(hash, out_data + plen, hlen) != 0 || + force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " - "received message"); + "received message (force_mac_error=%d)", + force_mac_error); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } + + *out_len = plen; + } else { + os_memcpy(out_data, in_data, in_len); + *out_len = in_len; } /* TLSCompressed must not be more than 2^14+1024 bytes */ diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h index 9c7c0a4..0d62816 100644 --- a/src/tls/tlsv1_record.h +++ b/src/tls/tlsv1_record.h @@ -1,6 +1,6 @@ /* - * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -35,6 +35,8 @@ enum { }; struct tlsv1_record_layer { + u16 tls_version; + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; u8 write_key[TLS_MAX_WRITE_KEY_LEN]; @@ -66,7 +68,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, - size_t buf_size, size_t payload_len, size_t *out_len); + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len); int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert); diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 6a61235..1f48aa5 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -201,10 +201,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); - os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, - out_data, out_len, in_len, &rlen) < 0) { + out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 49e811f..9ffe05c 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -85,15 +85,26 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_version = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", conn->client_version >> 8, conn->client_version & 0xff); - if (conn->client_version < TLS_VERSION) { + if (conn->client_version < TLS_VERSION_1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello"); + "ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + if (TLS_VERSION == TLS_VERSION_1) + conn->rl.tls_version = TLS_VERSION_1; + else if (conn->client_version > TLS_VERSION_1_1) + conn->rl.tls_version = TLS_VERSION_1_1; + else + conn->rl.tls_version = conn->client_version; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + conn->rl.tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -424,7 +435,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, } if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, 0) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); @@ -483,6 +494,14 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; + if (pos + encr_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " + "format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } outbuflen = outlen = end - pos; out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? @@ -512,21 +531,21 @@ static int tls_process_client_key_exchange_rsa( */ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, - pos, end - pos, + pos, encr_len, out, &outlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " - "PreMasterSecret (encr_len=%d outlen=%lu)", - (int) (end - pos), (unsigned long) outlen); + "PreMasterSecret (encr_len=%u outlen=%lu)", + encr_len, (unsigned long) outlen); use_random = 1; } - if (outlen != TLS_PRE_MASTER_SECRET_LEN) { + if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " "length %lu", (unsigned long) outlen); use_random = 1; } - if (WPA_GET_BE16(out) != conn->client_version) { + if (!use_random && WPA_GET_BE16(out) != conn->client_version) { wpa_printf(MSG_DEBUG, "TLSv1: Client version in " "ClientKeyExchange does not match with version in " "ClientHello"); diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 6d1df7f..63d70a2 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -1,6 +1,6 @@ /* - * TLSv1 server - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server - write handshake message + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -18,6 +18,7 @@ #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -58,7 +59,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, os_get_time(&now); WPA_PUT_BE32(conn->server_random, now.sec); - if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "server_random"); return -1; @@ -67,7 +68,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, conn->server_random, TLS_RANDOM_LEN); conn->session_id_len = TLS_SESSION_ID_MAX_LEN; - if (os_get_random(conn->session_id, conn->session_id_len)) { + if (random_get_bytes(conn->session_id, conn->session_id_len)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "session_id"); return -1; @@ -86,7 +87,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += 3; /* body - ServerHello */ /* ProtocolVersion server_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); @@ -142,7 +143,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn, tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -226,7 +228,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -287,7 +290,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(conn->dh_secret, conn->dh_secret_len)) { + if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -417,7 +420,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -482,7 +486,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -501,40 +506,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, static int tls_write_server_hello_done(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos; size_t rlen; - - pos = *msgpos; + u8 payload[4]; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ - hs_start = pos; + pos = payload; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, 0); pos += 3; /* body - ServerHelloDone (empty) */ - WPA_PUT_BE24(hs_length, pos - hs_length - 3); - if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, payload, pos - payload, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + tls_verify_hash_add(&conn->verify, payload, pos - payload); - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -543,17 +543,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -568,7 +567,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -577,9 +576,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, static int tls_write_server_finished(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; pos = *msgpos; @@ -612,40 +611,35 @@ static int tls_write_server_finished(struct tlsv1_server *conn, if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, - verify_data, TLS_VERIFY_DATA_LEN)) { + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", - verify_data, TLS_VERIFY_DATA_LEN); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; - os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); pos += TLS_VERIFY_DATA_LEN; - WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, - rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } - pos = rhdr + rlen; - - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -770,7 +764,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index bc93df6..347f975 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -1,6 +1,6 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -1834,7 +1834,7 @@ static int x509_valid_issuer(const struct x509_certificate *cert) */ int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, - int *reason) + int *reason, int disable_time_checks) { long unsigned idx; int chain_trusted = 0; @@ -1854,10 +1854,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, if (chain_trusted) continue; - if ((unsigned long) now.sec < - (unsigned long) cert->not_before || - (unsigned long) now.sec > - (unsigned long) cert->not_after) { + if (!disable_time_checks && + ((unsigned long) now.sec < + (unsigned long) cert->not_before || + (unsigned long) now.sec > + (unsigned long) cert->not_after)) { wpa_printf(MSG_INFO, "X509: Certificate not valid " "(now=%lu not_before=%lu not_after=%lu)", now.sec, cert->not_before, cert->not_after); diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h index 37292d7..3e2005b 100644 --- a/src/tls/x509v3.h +++ b/src/tls/x509v3.h @@ -1,6 +1,6 @@ /* * X.509v3 certificate parsing and processing - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * * 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 @@ -120,7 +120,7 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, - int *reason); + int *reason, int disable_time_checks); struct x509_certificate * x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); diff --git a/src/utils/Makefile b/src/utils/Makefile index 527cf3e..0f1f191 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -28,6 +28,9 @@ LIB_OBJS += os_unix.o # Pick correct event loop implementation LIB_OBJS += eloop.o +# Pick correct edit implementation +LIB_OBJS += edit.o + #LIB_OBJS += pcsc_funcs.o libutils.a: $(LIB_OBJS) diff --git a/src/utils/base64.c b/src/utils/base64.c index 155bfce..fb1224b 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,6 +1,6 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005, Jouni Malinen + * Copyright (c) 2005-2011, Jouni Malinen * * 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 @@ -103,8 +103,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len) { - unsigned char dtable[256], *out, *pos, in[4], block[4], tmp; + unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; + int pad = 0; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) @@ -131,7 +132,8 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, if (tmp == 0x80) continue; - in[count] = src[i]; + if (src[i] == '=') + pad++; block[count] = tmp; count++; if (count == 4) { @@ -139,16 +141,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *pos++ = (block[1] << 4) | (block[2] >> 2); *pos++ = (block[2] << 6) | block[3]; count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + os_free(out); + return NULL; + } + break; + } } } - if (pos > out) { - if (in[2] == '=') - pos -= 2; - else if (in[3] == '=') - pos--; - } - *out_len = pos - out; return out; } diff --git a/src/utils/base64.h b/src/utils/base64.h index 73312dd..b87a168 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -13,7 +13,7 @@ */ #ifndef BASE64_H -#define BASE64_h +#define BASE64_H unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); diff --git a/src/utils/common.c b/src/utils/common.c index 1b8ea80..89eca1c 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -29,7 +29,7 @@ static int hex2num(char c) } -static int hex2byte(const char *hex) +int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); @@ -69,6 +69,30 @@ int hwaddr_aton(const char *txt, u8 *addr) return 0; } +/** + * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format) + * @txt: MAC address as a string (e.g., "001122334455") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +int hwaddr_compact_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + } + + return 0; +} /** * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format) diff --git a/src/utils/common.h b/src/utils/common.h index f17bf69..14ab297 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -320,6 +320,9 @@ static inline unsigned int wpa_swap_32(unsigned int v) #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif +#ifndef ETH_P_80211_ENCAP +#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ +#endif #ifndef ETH_P_PAE #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ #endif /* ETH_P_PAE */ @@ -402,6 +405,12 @@ void perror(const char *s); #ifndef MAC2STR #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +/* + * Compact form for string representation of MAC address + * To be used, e.g., for constructing dbus paths for P2P Devices + */ +#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x" #endif #ifndef BIT @@ -436,7 +445,9 @@ typedef u64 __bitwise le64; #endif /* __must_check */ int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_compact_aton(const char *txt, u8 *addr); int hwaddr_aton2(const char *txt, u8 *addr); +int hex2byte(const char *hex); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); @@ -459,6 +470,13 @@ static inline int is_zero_ether_addr(const u8 *a) return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); } +static inline int is_broadcast_ether_addr(const u8 *a) +{ + return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; +} + +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" + #include "wpa_debug.h" @@ -474,4 +492,11 @@ static inline int is_zero_ether_addr(const u8 *a) void * __hide_aliasing_typecast(void *foo); #define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) +#ifdef CONFIG_VALGRIND +#include +#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len)) +#else /* CONFIG_VALGRIND */ +#define WPA_MEM_DEFINED(ptr, len) do { } while (0) +#endif /* CONFIG_VALGRIND */ + #endif /* COMMON_H */ diff --git a/src/utils/edit.c b/src/utils/edit.c new file mode 100644 index 0000000..c5b17ac --- /dev/null +++ b/src/utils/edit.c @@ -0,0 +1,1178 @@ +/* + * Command line editing and history + * Copyright (c) 2010-2011, Jouni Malinen + * + * 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 + +#include "common.h" +#include "eloop.h" +#include "list.h" +#include "edit.h" + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; +static int cmdbuf_len = 0; +static char currbuf[CMD_BUF_LEN]; +static int currbuf_valid = 0; + +#define HISTORY_MAX 100 + +struct edit_history { + struct dl_list list; + char str[1]; +}; + +static struct dl_list history_list; +static struct edit_history *history_curr; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static struct termios prevt, newt; + + +#define CLEAR_END_LINE "\e[K" + + +void edit_clear_line(void) +{ + int i; + putchar('\r'); + for (i = 0; i < cmdbuf_len + 2; i++) + putchar(' '); +} + + +static void move_start(void) +{ + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void move_end(void) +{ + cmdbuf_pos = cmdbuf_len; + edit_redraw(); +} + + +static void move_left(void) +{ + if (cmdbuf_pos > 0) { + cmdbuf_pos--; + edit_redraw(); + } +} + + +static void move_right(void) +{ + if (cmdbuf_pos < cmdbuf_len) { + cmdbuf_pos++; + edit_redraw(); + } +} + + +static void move_word_left(void) +{ + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') + cmdbuf_pos--; + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') + cmdbuf_pos--; + edit_redraw(); +} + + +static void move_word_right(void) +{ + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') + cmdbuf_pos++; + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') + cmdbuf_pos++; + edit_redraw(); +} + + +static void delete_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf_pos--; + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_current(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, + cmdbuf_len - cmdbuf_pos); + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_word(void) +{ + int pos; + + edit_clear_line(); + pos = cmdbuf_pos; + while (pos > 0 && cmdbuf[pos - 1] == ' ') + pos--; + while (pos > 0 && cmdbuf[pos - 1] != ' ') + pos--; + os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos - pos; + cmdbuf_pos = pos; + edit_redraw(); +} + + +static void clear_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos; + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void clear_right(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + cmdbuf_len = cmdbuf_pos; + edit_redraw(); +} + + +static void history_add(const char *str) +{ + struct edit_history *h, *match = NULL, *last = NULL; + size_t len, count = 0; + + if (str[0] == '\0') + return; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strcmp(str, h->str) == 0) { + match = h; + break; + } + last = h; + count++; + } + + if (match) { + dl_list_del(&h->list); + dl_list_add(&history_list, &h->list); + history_curr = h; + return; + } + + if (count >= HISTORY_MAX && last) { + dl_list_del(&last->list); + os_free(last); + } + + len = os_strlen(str); + h = os_zalloc(sizeof(*h) + len); + if (h == NULL) + return; + dl_list_add(&history_list, &h->list); + os_strlcpy(h->str, str, len + 1); + history_curr = h; +} + + +static void history_use(void) +{ + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); + os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); + edit_redraw(); +} + + +static void history_prev(void) +{ + if (history_curr == NULL) + return; + + if (history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (!currbuf_valid) { + cmdbuf[cmdbuf_len] = '\0'; + os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); + currbuf_valid = 1; + history_use(); + return; + } + } + + if (history_curr == + dl_list_last(&history_list, struct edit_history, list)) + return; + + history_curr = dl_list_entry(history_curr->list.next, + struct edit_history, list); + history_use(); +} + + +static void history_next(void) +{ + if (history_curr == NULL || + history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (currbuf_valid) { + currbuf_valid = 0; + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); + os_memcpy(cmdbuf, currbuf, cmdbuf_len); + edit_redraw(); + } + return; + } + + history_curr = dl_list_entry(history_curr->list.prev, + struct edit_history, list); + history_use(); +} + + +static void history_read(const char *fname) +{ + FILE *f; + char buf[CMD_BUF_LEN], *pos; + + f = fopen(fname, "r"); + if (f == NULL) + return; + + while (fgets(buf, CMD_BUF_LEN, f)) { + for (pos = buf; *pos; pos++) { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + } + history_add(buf); + } + + fclose(f); +} + + +static void history_write(const char *fname, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + FILE *f; + struct edit_history *h; + + f = fopen(fname, "w"); + if (f == NULL) + return; + + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { + if (filter_cb && filter_cb(edit_cb_ctx, h->str)) + continue; + fprintf(f, "%s\n", h->str); + } + + fclose(f); +} + + +static void history_debug_dump(void) +{ + struct edit_history *h; + edit_clear_line(); + printf("\r"); + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) + printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); + if (currbuf_valid) + printf("{%s}\n", currbuf); + edit_redraw(); +} + + +static void insert_char(int c) +{ + if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) + return; + if (cmdbuf_len == cmdbuf_pos) { + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + putchar(c); + fflush(stdout); + } else { + os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + edit_redraw(); + } +} + + +static void process_cmd(void) +{ + + if (cmdbuf_len == 0) { + printf("\n> "); + fflush(stdout); + return; + } + printf("\n"); + cmdbuf[cmdbuf_len] = '\0'; + history_add(cmdbuf); + cmdbuf_pos = 0; + cmdbuf_len = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("> "); + fflush(stdout); +} + + +static void free_completions(char **c) +{ + int i; + if (c == NULL) + return; + for (i = 0; c[i]; i++) + os_free(c[i]); + os_free(c); +} + + +static int filter_strings(char **c, char *str, size_t len) +{ + int i, j; + + for (i = 0, j = 0; c[j]; j++) { + if (os_strncasecmp(c[j], str, len) == 0) { + if (i != j) { + c[i] = c[j]; + c[j] = NULL; + } + i++; + } else { + os_free(c[j]); + c[j] = NULL; + } + } + c[i] = NULL; + return i; +} + + +static int common_len(const char *a, const char *b) +{ + int len = 0; + while (a[len] && a[len] == b[len]) + len++; + return len; +} + + +static int max_common_length(char **c) +{ + int len, i; + + len = os_strlen(c[0]); + for (i = 1; c[i]; i++) { + int same = common_len(c[0], c[i]); + if (same < len) + len = same; + } + + return len; +} + + +static int cmp_str(const void *a, const void *b) +{ + return os_strcmp(* (const char **) a, * (const char **) b); +} + +static void complete(int list) +{ + char **c; + int i, len, count; + int start, end; + int room, plen, add_space; + + if (edit_completion_cb == NULL) + return; + + cmdbuf[cmdbuf_len] = '\0'; + c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); + if (c == NULL) + return; + + end = cmdbuf_pos; + start = end; + while (start > 0 && cmdbuf[start - 1] != ' ') + start--; + plen = end - start; + + count = filter_strings(c, &cmdbuf[start], plen); + if (count == 0) { + free_completions(c); + return; + } + + len = max_common_length(c); + if (len <= plen && count > 1) { + if (list) { + qsort(c, count, sizeof(char *), cmp_str); + edit_clear_line(); + printf("\r"); + for (i = 0; c[i]; i++) + printf("%s%s", i > 0 ? " " : "", c[i]); + printf("\n"); + edit_redraw(); + } + free_completions(c); + return; + } + len -= plen; + + room = sizeof(cmdbuf) - 1 - cmdbuf_len; + if (room < len) + len = room; + add_space = count == 1 && len < room; + + os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); + if (add_space) + cmdbuf[cmdbuf_pos + len] = ' '; + + cmdbuf_pos += len + add_space; + cmdbuf_len += len + add_space; + + edit_redraw(); + + free_completions(c); +} + + +enum edit_key_code { + EDIT_KEY_NONE = 256, + EDIT_KEY_TAB, + EDIT_KEY_UP, + EDIT_KEY_DOWN, + EDIT_KEY_RIGHT, + EDIT_KEY_LEFT, + EDIT_KEY_ENTER, + EDIT_KEY_BACKSPACE, + EDIT_KEY_INSERT, + EDIT_KEY_DELETE, + EDIT_KEY_HOME, + EDIT_KEY_END, + EDIT_KEY_PAGE_UP, + EDIT_KEY_PAGE_DOWN, + EDIT_KEY_F1, + EDIT_KEY_F2, + EDIT_KEY_F3, + EDIT_KEY_F4, + EDIT_KEY_F5, + EDIT_KEY_F6, + EDIT_KEY_F7, + EDIT_KEY_F8, + EDIT_KEY_F9, + EDIT_KEY_F10, + EDIT_KEY_F11, + EDIT_KEY_F12, + EDIT_KEY_CTRL_UP, + EDIT_KEY_CTRL_DOWN, + EDIT_KEY_CTRL_RIGHT, + EDIT_KEY_CTRL_LEFT, + EDIT_KEY_CTRL_A, + EDIT_KEY_CTRL_B, + EDIT_KEY_CTRL_D, + EDIT_KEY_CTRL_E, + EDIT_KEY_CTRL_F, + EDIT_KEY_CTRL_G, + EDIT_KEY_CTRL_H, + EDIT_KEY_CTRL_J, + EDIT_KEY_CTRL_K, + EDIT_KEY_CTRL_L, + EDIT_KEY_CTRL_N, + EDIT_KEY_CTRL_O, + EDIT_KEY_CTRL_P, + EDIT_KEY_CTRL_R, + EDIT_KEY_CTRL_T, + EDIT_KEY_CTRL_U, + EDIT_KEY_CTRL_V, + EDIT_KEY_CTRL_W, + EDIT_KEY_ALT_UP, + EDIT_KEY_ALT_DOWN, + EDIT_KEY_ALT_RIGHT, + EDIT_KEY_ALT_LEFT, + EDIT_KEY_SHIFT_UP, + EDIT_KEY_SHIFT_DOWN, + EDIT_KEY_SHIFT_RIGHT, + EDIT_KEY_SHIFT_LEFT, + EDIT_KEY_ALT_SHIFT_UP, + EDIT_KEY_ALT_SHIFT_DOWN, + EDIT_KEY_ALT_SHIFT_RIGHT, + EDIT_KEY_ALT_SHIFT_LEFT, + EDIT_KEY_EOF +}; + +static void show_esc_buf(const char *esc_buf, char c, int i) +{ + edit_clear_line(); + printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); + edit_redraw(); +} + + +static enum edit_key_code esc_seq_to_key1_no(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_UP; + case 'B': + return EDIT_KEY_DOWN; + case 'C': + return EDIT_KEY_RIGHT; + case 'D': + return EDIT_KEY_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_SHIFT_UP; + case 'B': + return EDIT_KEY_SHIFT_DOWN; + case 'C': + return EDIT_KEY_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_UP; + case 'B': + return EDIT_KEY_ALT_DOWN; + case 'C': + return EDIT_KEY_ALT_RIGHT; + case 'D': + return EDIT_KEY_ALT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_SHIFT_UP; + case 'B': + return EDIT_KEY_ALT_SHIFT_DOWN; + case 'C': + return EDIT_KEY_ALT_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_ALT_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_ctrl(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_CTRL_UP; + case 'B': + return EDIT_KEY_CTRL_DOWN; + case 'C': + return EDIT_KEY_CTRL_RIGHT; + case 'D': + return EDIT_KEY_CTRL_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) +{ + /* ESC-[; */ + + if (param1 < 0 && param2 < 0) + return esc_seq_to_key1_no(last); + + if (param1 == 1 && param2 == 2) + return esc_seq_to_key1_shift(last); + + if (param1 == 1 && param2 == 3) + return esc_seq_to_key1_alt(last); + + if (param1 == 1 && param2 == 4) + return esc_seq_to_key1_alt_shift(last); + + if (param1 == 1 && param2 == 5) + return esc_seq_to_key1_ctrl(last); + + if (param2 < 0) { + if (last != '~') + return EDIT_KEY_NONE; + switch (param1) { + case 2: + return EDIT_KEY_INSERT; + case 3: + return EDIT_KEY_DELETE; + case 5: + return EDIT_KEY_PAGE_UP; + case 6: + return EDIT_KEY_PAGE_DOWN; + case 15: + return EDIT_KEY_F5; + case 17: + return EDIT_KEY_F6; + case 18: + return EDIT_KEY_F7; + case 19: + return EDIT_KEY_F8; + case 20: + return EDIT_KEY_F9; + case 21: + return EDIT_KEY_F10; + case 23: + return EDIT_KEY_F11; + case 24: + return EDIT_KEY_F12; + } + } + + return EDIT_KEY_NONE; +} + + +static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) +{ + /* ESC-O; */ + + if (param1 >= 0 || param2 >= 0) + return EDIT_KEY_NONE; + + switch (last) { + case 'F': + return EDIT_KEY_END; + case 'H': + return EDIT_KEY_HOME; + case 'P': + return EDIT_KEY_F1; + case 'Q': + return EDIT_KEY_F2; + case 'R': + return EDIT_KEY_F3; + case 'S': + return EDIT_KEY_F4; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key(char *seq) +{ + char last, *pos; + int param1 = -1, param2 = -1; + enum edit_key_code ret = EDIT_KEY_NONE; + + last = '\0'; + for (pos = seq; *pos; pos++) + last = *pos; + + if (seq[1] >= '0' && seq[1] <= '9') { + param1 = atoi(&seq[1]); + pos = os_strchr(seq, ';'); + if (pos) + param2 = atoi(pos + 1); + } + + if (seq[0] == '[') + ret = esc_seq_to_key1(param1, param2, last); + else if (seq[0] == 'O') + ret = esc_seq_to_key2(param1, param2, last); + + if (ret != EDIT_KEY_NONE) + return ret; + + edit_clear_line(); + printf("\rUnknown escape sequence '%s'\n", seq); + edit_redraw(); + return EDIT_KEY_NONE; +} + + +static enum edit_key_code edit_read_key(int sock) +{ + int c; + unsigned char buf[1]; + int res; + static int esc = -1; + static char esc_buf[7]; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) + return EDIT_KEY_EOF; + + c = buf[0]; + + if (esc >= 0) { + if (c == 27 /* ESC */) { + esc = 0; + return EDIT_KEY_NONE; + } + + if (esc == 6) { + show_esc_buf(esc_buf, c, 0); + esc = -1; + } else { + esc_buf[esc++] = c; + esc_buf[esc] = '\0'; + } + } + + if (esc == 1) { + if (esc_buf[0] != '[' && esc_buf[0] != 'O') { + show_esc_buf(esc_buf, c, 1); + esc = -1; + return EDIT_KEY_NONE; + } else + return EDIT_KEY_NONE; /* Escape sequence continues */ + } + + if (esc > 1) { + if ((c >= '0' && c <= '9') || c == ';') + return EDIT_KEY_NONE; /* Escape sequence continues */ + + if (c == '~' || (c >= 'A' && c <= 'Z')) { + esc = -1; + return esc_seq_to_key(esc_buf); + } + + show_esc_buf(esc_buf, c, 2); + esc = -1; + return EDIT_KEY_NONE; + } + + switch (c) { + case 1: + return EDIT_KEY_CTRL_A; + case 2: + return EDIT_KEY_CTRL_B; + case 4: + return EDIT_KEY_CTRL_D; + case 5: + return EDIT_KEY_CTRL_E; + case 6: + return EDIT_KEY_CTRL_F; + case 7: + return EDIT_KEY_CTRL_G; + case 8: + return EDIT_KEY_CTRL_H; + case 9: + return EDIT_KEY_TAB; + case 10: + return EDIT_KEY_CTRL_J; + case 13: /* CR */ + return EDIT_KEY_ENTER; + case 11: + return EDIT_KEY_CTRL_K; + case 12: + return EDIT_KEY_CTRL_L; + case 14: + return EDIT_KEY_CTRL_N; + case 15: + return EDIT_KEY_CTRL_O; + case 16: + return EDIT_KEY_CTRL_P; + case 18: + return EDIT_KEY_CTRL_R; + case 20: + return EDIT_KEY_CTRL_T; + case 21: + return EDIT_KEY_CTRL_U; + case 22: + return EDIT_KEY_CTRL_V; + case 23: + return EDIT_KEY_CTRL_W; + case 27: /* ESC */ + esc = 0; + return EDIT_KEY_NONE; + case 127: + return EDIT_KEY_BACKSPACE; + default: + return c; + } +} + + +static char search_buf[21]; +static int search_skip; + +static char * search_find(void) +{ + struct edit_history *h; + size_t len = os_strlen(search_buf); + int skip = search_skip; + + if (len == 0) + return NULL; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strstr(h->str, search_buf)) { + if (skip == 0) + return h->str; + skip--; + } + } + + search_skip = 0; + return NULL; +} + + +static void search_redraw(void) +{ + char *match = search_find(); + printf("\rsearch '%s': %s" CLEAR_END_LINE, + search_buf, match ? match : ""); + printf("\rsearch '%s", search_buf); + fflush(stdout); +} + + +static void search_start(void) +{ + edit_clear_line(); + search_buf[0] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_clear(void) +{ + search_redraw(); + printf("\r" CLEAR_END_LINE); +} + + +static void search_stop(void) +{ + char *match = search_find(); + search_buf[0] = '\0'; + search_clear(); + if (match) { + os_strlcpy(cmdbuf, match, CMD_BUF_LEN); + cmdbuf_len = os_strlen(cmdbuf); + cmdbuf_pos = cmdbuf_len; + } + edit_redraw(); +} + + +static void search_cancel(void) +{ + search_buf[0] = '\0'; + search_clear(); + edit_redraw(); +} + + +static void search_backspace(void) +{ + size_t len; + len = os_strlen(search_buf); + if (len == 0) + return; + search_buf[len - 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_next(void) +{ + search_skip++; + search_find(); + search_redraw(); +} + + +static void search_char(char c) +{ + size_t len; + len = os_strlen(search_buf); + if (len == sizeof(search_buf) - 1) + return; + search_buf[len] = c; + search_buf[len + 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static enum edit_key_code search_key(enum edit_key_code c) +{ + switch (c) { + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + case EDIT_KEY_LEFT: + case EDIT_KEY_RIGHT: + case EDIT_KEY_HOME: + case EDIT_KEY_END: + case EDIT_KEY_CTRL_A: + case EDIT_KEY_CTRL_E: + search_stop(); + return c; + case EDIT_KEY_DOWN: + case EDIT_KEY_UP: + search_cancel(); + return EDIT_KEY_EOF; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + search_backspace(); + break; + case EDIT_KEY_CTRL_R: + search_next(); + break; + default: + if (c >= 32 && c <= 255) + search_char(c); + break; + } + + return EDIT_KEY_NONE; +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + static int last_tab = 0; + static int search = 0; + enum edit_key_code c; + + c = edit_read_key(sock); + + if (search) { + c = search_key(c); + if (c == EDIT_KEY_NONE) + return; + search = 0; + if (c == EDIT_KEY_EOF) + return; + } + + if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) + last_tab = 0; + + switch (c) { + case EDIT_KEY_NONE: + break; + case EDIT_KEY_EOF: + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_TAB: + complete(last_tab); + last_tab = 1; + break; + case EDIT_KEY_UP: + case EDIT_KEY_CTRL_P: + history_prev(); + break; + case EDIT_KEY_DOWN: + case EDIT_KEY_CTRL_N: + history_next(); + break; + case EDIT_KEY_RIGHT: + case EDIT_KEY_CTRL_F: + move_right(); + break; + case EDIT_KEY_LEFT: + case EDIT_KEY_CTRL_B: + move_left(); + break; + case EDIT_KEY_CTRL_RIGHT: + move_word_right(); + break; + case EDIT_KEY_CTRL_LEFT: + move_word_left(); + break; + case EDIT_KEY_DELETE: + delete_current(); + break; + case EDIT_KEY_END: + move_end(); + break; + case EDIT_KEY_HOME: + case EDIT_KEY_CTRL_A: + move_start(); + break; + case EDIT_KEY_F2: + history_debug_dump(); + break; + case EDIT_KEY_CTRL_D: + if (cmdbuf_len > 0) { + delete_current(); + return; + } + printf("\n"); + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_CTRL_E: + move_end(); + break; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + delete_left(); + break; + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + process_cmd(); + break; + case EDIT_KEY_CTRL_K: + clear_right(); + break; + case EDIT_KEY_CTRL_L: + edit_clear_line(); + edit_redraw(); + break; + case EDIT_KEY_CTRL_R: + search = 1; + search_start(); + break; + case EDIT_KEY_CTRL_U: + clear_left(); + break; + case EDIT_KEY_CTRL_W: + delete_word(); + break; + default: + if (c >= 32 && c <= 255) + insert_char(c); + break; + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file) +{ + currbuf[0] = '\0'; + dl_list_init(&history_list); + history_curr = NULL; + if (history_file) + history_read(history_file); + + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + tcgetattr(STDIN_FILENO, &prevt); + newt = prevt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + printf("> "); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + struct edit_history *h; + if (history_file) + history_write(history_file, filter_cb); + while ((h = dl_list_first(&history_list, struct edit_history, list))) { + dl_list_del(&h->list); + os_free(h); + } + edit_clear_line(); + putchar('\r'); + fflush(stdout); + eloop_unregister_read_sock(STDIN_FILENO); + tcsetattr(STDIN_FILENO, TCSANOW, &prevt); +} + + +void edit_redraw(void) +{ + char tmp; + cmdbuf[cmdbuf_len] = '\0'; + printf("\r> %s", cmdbuf); + if (cmdbuf_pos != cmdbuf_len) { + tmp = cmdbuf[cmdbuf_pos]; + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r> %s", cmdbuf); + cmdbuf[cmdbuf_pos] = tmp; + } + fflush(stdout); +} diff --git a/src/utils/edit.h b/src/utils/edit.h new file mode 100644 index 0000000..fc4474b --- /dev/null +++ b/src/utils/edit.h @@ -0,0 +1,27 @@ +/* + * Command line editing and history + * Copyright (c) 2010, Jouni Malinen + * + * 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. + */ + +#ifndef EDIT_H +#define EDIT_H + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file); +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)); +void edit_clear_line(void); +void edit_redraw(void); + +#endif /* EDIT_H */ diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c new file mode 100644 index 0000000..1fef7b9 --- /dev/null +++ b/src/utils/edit_readline.c @@ -0,0 +1,184 @@ +/* + * Command line editing and history wrapper for readline + * Copyright (c) 2010, Jouni Malinen + * + * 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 +#include + +#include "common.h" +#include "eloop.h" +#include "edit.h" + + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static char **pending_completions = NULL; + + +static void readline_free_completions(void) +{ + int i; + if (pending_completions == NULL) + return; + for (i = 0; pending_completions[i]; i++) + os_free(pending_completions[i]); + os_free(pending_completions); + pending_completions = NULL; +} + + +static char * readline_completion_func(const char *text, int state) +{ + static int pos = 0; + static size_t len = 0; + + if (pending_completions == NULL) { + rl_attempted_completion_over = 1; + return NULL; + } + + if (state == 0) { + pos = 0; + len = os_strlen(text); + } + for (; pending_completions[pos]; pos++) { + if (strncmp(pending_completions[pos], text, len) == 0) + return strdup(pending_completions[pos++]); + } + + rl_attempted_completion_over = 1; + return NULL; +} + + +static char ** readline_completion(const char *text, int start, int end) +{ + readline_free_completions(); + if (edit_completion_cb) + pending_completions = edit_completion_cb(edit_cb_ctx, + rl_line_buffer, end); + return rl_completion_matches(text, readline_completion_func); +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + rl_callback_read_char(); +} + + +static void trunc_nl(char *str) +{ + char *pos = str; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } +} + + +static void readline_cmd_handler(char *cmd) +{ + if (cmd && *cmd) { + HIST_ENTRY *h; + while (next_history()) + ; + h = previous_history(); + if (h == NULL || os_strcmp(cmd, h->line) != 0) + add_history(cmd); + next_history(); + } + if (cmd == NULL) { + edit_eof_cb(edit_cb_ctx); + return; + } + trunc_nl(cmd); + edit_cmd_cb(edit_cb_ctx, cmd); +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + rl_attempted_completion_function = readline_completion; + if (history_file) { + read_history(history_file); + stifle_history(100); + } + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + rl_callback_handler_install("> ", readline_cmd_handler); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + rl_callback_handler_remove(); + readline_free_completions(); + + eloop_unregister_read_sock(STDIN_FILENO); + + if (history_file) { + /* Save command history, excluding lines that may contain + * passwords. */ + HIST_ENTRY *h; + history_set_pos(0); + while ((h = current_history())) { + char *p = h->line; + while (*p == ' ' || *p == '\t') + p++; + if (filter_cb && filter_cb(edit_cb_ctx, p)) { + h = remove_history(where_history()); + if (h) { + os_free(h->line); + free(h->data); + os_free(h); + } else + next_history(); + } else + next_history(); + } + write_history(history_file); + } +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + rl_on_new_line(); + rl_redisplay(); +} diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c new file mode 100644 index 0000000..61fb24e --- /dev/null +++ b/src/utils/edit_simple.c @@ -0,0 +1,96 @@ +/* + * Minimal command line editing + * Copyright (c) 2010, Jouni Malinen + * + * 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 "eloop.h" +#include "edit.h" + + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + int c; + unsigned char buf[1]; + int res; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) { + edit_eof_cb(edit_cb_ctx); + return; + } + c = buf[0]; + + if (c == '\r' || c == '\n') { + cmdbuf[cmdbuf_pos] = '\0'; + cmdbuf_pos = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("> "); + fflush(stdout); + return; + } + + if (c >= 32 && c <= 255) { + if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { + cmdbuf[cmdbuf_pos++] = c; + } + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + printf("> "); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + eloop_unregister_read_sock(STDIN_FILENO); +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r> %s", cmdbuf); +} diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 4b61598..b550c63 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -300,6 +300,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) @@ -308,7 +309,18 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, os_free(timeout); return -1; } + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; diff --git a/src/utils/eloop.h b/src/utils/eloop.h index 1228f24..a656bf8 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -144,7 +144,7 @@ void eloop_unregister_sock(int sock, eloop_event_type type); * Returns: 0 on success, -1 on failure * * Register an event handler for the given event. This function is used to - * register eloop implementation specific events which are mainly targetted for + * register eloop implementation specific events which are mainly targeted for * operating system specific code (driver interface and l2_packet) since the * portable code will not be able to use such an OS-specific call. The handler * function will be called whenever the event is triggered. The handler diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c index 94cc72d..c726ece 100644 --- a/src/utils/eloop_win.c +++ b/src/utils/eloop_win.c @@ -243,12 +243,24 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp, *prev; + os_time_t now_sec; timeout = os_malloc(sizeof(*timeout)); if (timeout == NULL) return -1; os_get_time(&timeout->time); + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; diff --git a/src/utils/includes.h b/src/utils/includes.h index 63b5c23..cf2a42b 100644 --- a/src/utils/includes.h +++ b/src/utils/includes.h @@ -34,7 +34,6 @@ #include #endif /* _WIN32_WCE */ #include -#include #ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER diff --git a/src/utils/list.h b/src/utils/list.h index ed7c022..c8dccee 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -75,6 +75,10 @@ static inline unsigned int dl_list_len(struct dl_list *list) (dl_list_empty((list)) ? NULL : \ dl_list_entry((list)->next, type, member)) +#define dl_list_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, type, member)) + #define dl_list_for_each(item, list, type, member) \ for (item = dl_list_entry((list)->next, type, member); \ &item->member != (list); \ @@ -86,4 +90,12 @@ static inline unsigned int dl_list_len(struct dl_list *list) &item->member != (list); \ item = n, n = dl_list_entry(n->member.next, type, member)) +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + #endif /* LIST_H */ diff --git a/src/utils/os.h b/src/utils/os.h index f4723d8..f69478a 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -70,6 +70,16 @@ int os_get_time(struct os_time *t); int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t); +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); /** * os_daemonize - Run in the background (detach from the controlling terminal) diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index 5260e23..8024a30 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -70,6 +70,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { diff --git a/src/utils/os_none.c b/src/utils/os_none.c index bab8f17..3fbb777 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -38,6 +38,11 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, return -1; } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + return -1; +} + int os_daemonize(const char *pid_file) { diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 6f58fa4..b546201 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -14,6 +14,14 @@ #include "includes.h" +#include + +#ifdef ANDROID +#include +#include +#include +#endif /* ANDROID */ + #include "os.h" #ifdef WPA_TRACE @@ -98,6 +106,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + #ifdef __APPLE__ #include static int os_daemon(int nochdir, int noclose) @@ -135,9 +161,9 @@ static int os_daemon(int nochdir, int noclose) int os_daemonize(const char *pid_file) { -#ifdef __uClinux__ +#if defined(__uClinux__) || defined(__sun__) return -1; -#else /* __uClinux__ */ +#else /* defined(__uClinux__) || defined(__sun__) */ if (os_daemon(0, 0)) { perror("daemon"); return -1; @@ -152,7 +178,7 @@ int os_daemonize(const char *pid_file) } return -0; -#endif /* __uClinux__ */ +#endif /* defined(__uClinux__) || defined(__sun__) */ } @@ -232,6 +258,30 @@ char * os_rel2abs_path(const char *rel_path) int os_program_init(void) { +#ifdef ANDROID + /* + * We ignore errors here since errors are normal if we + * are already running as non-root. + */ + gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + setgid(AID_WIFI); + setuid(AID_WIFI); + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + cap.effective = cap.permitted = + (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); + cap.inheritable = 0; + capset(&header, &cap); +#endif /* ANDROID */ + #ifdef WPA_TRACE dl_list_init(&alloc_list); #endif /* WPA_TRACE */ @@ -285,14 +335,21 @@ char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; + long pos; f = fopen(name, "rb"); if (f == NULL) return NULL; - fseek(f, 0, SEEK_END); - *len = ftell(f); - fseek(f, 0, SEEK_SET); + if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) { + fclose(f); + return NULL; + } + *len = pos; + if (fseek(f, 0, SEEK_SET) < 0) { + fclose(f); + return NULL; + } buf = os_malloc(*len); if (buf == NULL) { diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c index 0740964..51bd545 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -13,6 +13,7 @@ */ #include "includes.h" +#include #include #include @@ -92,6 +93,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { /* TODO */ diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index bf9f04a..c36193e 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -812,7 +812,7 @@ static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", buf, blen); - if (blen < 2 || buf[0] != 0x6c) { + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " "length determination"); return -1; diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h index 92a798a..2e0e872 100644 --- a/src/utils/radiotap_iter.h +++ b/src/utils/radiotap_iter.h @@ -1,3 +1,18 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green + * + * 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. + */ + #ifndef __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 6f6fc69..c3fd4cf 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -23,16 +23,46 @@ static int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ -#ifdef CONFIG_DEBUG_FILE -static FILE *out_file = NULL; -#endif /* CONFIG_DEBUG_FILE */ int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; int wpa_debug_timestamp = 0; +#ifdef CONFIG_ANDROID_LOG + +#include + +#ifndef ANDROID_LOG_NAME +#define ANDROID_LOG_NAME "wpa_supplicant" +#endif /* ANDROID_LOG_NAME */ + +void android_printf(int level, char *format, ...) +{ + if (level >= wpa_debug_level) { + va_list ap; + if (level == MSG_ERROR) + level = ANDROID_LOG_ERROR; + else if (level == MSG_WARNING) + level = ANDROID_LOG_WARN; + else if (level == MSG_INFO) + level = ANDROID_LOG_INFO; + else + level = ANDROID_LOG_DEBUG; + va_start(ap, format); + __android_log_vprint(level, ANDROID_LOG_NAME, format, ap); + va_end(ap); + } +} + +#else /* CONFIG_ANDROID_LOG */ + #ifndef CONFIG_NO_STDOUT_DEBUG +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ + + void wpa_debug_print_timestamp(void) { struct os_time tv; @@ -52,9 +82,13 @@ void wpa_debug_print_timestamp(void) #ifdef CONFIG_DEBUG_SYSLOG +#ifndef LOG_HOSTAPD +#define LOG_HOSTAPD LOG_DAEMON +#endif /* LOG_HOSTAPD */ + void wpa_debug_open_syslog(void) { - openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD); wpa_debug_syslog++; } @@ -132,6 +166,38 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, size_t i; if (level < wpa_debug_level) return; +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + const char *display; + char *strbuf = NULL; + + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + strbuf = os_malloc(1 + 3 * len); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < len; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", + title, len, display); + os_free(strbuf); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -273,11 +339,43 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, } +#ifdef CONFIG_DEBUG_FILE +static char *last_path = NULL; +#endif /* CONFIG_DEBUG_FILE */ + +int wpa_debug_reopen_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + int rv; + if (last_path) { + char *tmp = os_strdup(last_path); + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); + } else { + wpa_printf(MSG_ERROR, "Last-path was not set, cannot " + "re-open log file."); + rv = -1; + } + return rv; +#else /* CONFIG_DEBUG_FILE */ + return 0; +#endif /* CONFIG_DEBUG_FILE */ +} + + int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE if (!path) return 0; + + if (last_path == NULL || os_strcmp(last_path, path) != 0) { + /* Save our path to enable re-open */ + os_free(last_path); + last_path = os_strdup(path); + } + out_file = fopen(path, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " @@ -299,11 +397,14 @@ void wpa_debug_close_file(void) return; fclose(out_file); out_file = NULL; + os_free(last_path); + last_path = NULL; #endif /* CONFIG_DEBUG_FILE */ } #endif /* CONFIG_NO_STDOUT_DEBUG */ +#endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_WPA_MSG static wpa_msg_cb_func wpa_msg_cb = NULL; @@ -314,12 +415,21 @@ void wpa_msg_register_cb(wpa_msg_cb_func func) } +static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL; + +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func) +{ + wpa_msg_ifname_cb = func; +} + + void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; + char prefix[130]; buf = os_malloc(buflen); if (buf == NULL) { @@ -328,9 +438,19 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) return; } va_start(ap, fmt); + prefix[0] = '\0'; + if (wpa_msg_ifname_cb) { + const char *ifname = wpa_msg_ifname_cb(ctx); + if (ifname) { + int res = os_snprintf(prefix, sizeof(prefix), "%s: ", + ifname); + if (res < 0 || res >= (int) sizeof(prefix)) + prefix[0] = '\0'; + } + } len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_printf(level, "%s", buf); + wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) wpa_msg_cb(ctx, level, buf, len); os_free(buf); diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 6e5e79e..ae36afe 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -20,7 +20,35 @@ /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ -enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; +enum { + MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR +}; + +#ifdef CONFIG_ANDROID_LOG + +#define wpa_debug_print_timestamp() do {} while (0) +#define wpa_hexdump(...) do {} while (0) +#define wpa_hexdump_key(...) do {} while (0) +#define wpa_hexdump_buf(l,t,b) do {} while (0) +#define wpa_hexdump_buf_key(l,t,b) do {} while (0) +#define wpa_hexdump_ascii(...) do {} while (0) +#define wpa_hexdump_ascii_key(...) do {} while (0) +#define wpa_debug_open_file(...) do {} while (0) +#define wpa_debug_close_file() do {} while (0) +#define wpa_dbg(...) do {} while (0) + +static inline int wpa_debug_reopen_file(void) +{ + return 0; +} + + +void android_printf(int level, char *format, ...) +PRINTF_FORMAT(2, 3); + +#define wpa_printf android_printf + +#else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_NO_STDOUT_DEBUG @@ -34,10 +62,17 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) #define wpa_debug_open_file(p) do { } while (0) #define wpa_debug_close_file() do { } while (0) +#define wpa_dbg(args...) do { } while (0) + +static inline int wpa_debug_reopen_file(void) +{ + return 0; +} #else /* CONFIG_NO_STDOUT_DEBUG */ int wpa_debug_open_file(const char *path); +int wpa_debug_reopen_file(void); void wpa_debug_close_file(void); /** @@ -79,7 +114,8 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); static inline void wpa_hexdump_buf(int level, const char *title, const struct wpabuf *buf) { - wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf)); + wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); } /** @@ -100,7 +136,8 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); static inline void wpa_hexdump_buf_key(int level, const char *title, const struct wpabuf *buf) { - wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf)); + wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : 0, + buf ? wpabuf_len(buf) : 0); } /** @@ -136,13 +173,24 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len); +/* + * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce + * binary size. As such, it should be used with debugging messages that are not + * needed in the control interface while wpa_msg() has to be used for anything + * that needs to shown to control interface monitors. + */ +#define wpa_dbg(args...) wpa_msg(args) + #endif /* CONFIG_NO_STDOUT_DEBUG */ +#endif /* CONFIG_ANDROID_LOG */ + #ifdef CONFIG_NO_WPA_MSG #define wpa_msg(args...) do { } while (0) #define wpa_msg_ctrl(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) +#define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ /** * wpa_msg - Conditional printf for default target and ctrl_iface monitors @@ -183,8 +231,11 @@ typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, * @func: Callback function (%NULL to unregister) */ void wpa_msg_register_cb(wpa_msg_cb_func func); -#endif /* CONFIG_NO_WPA_MSG */ +typedef const char * (*wpa_msg_get_ifname_func)(void *ctx); +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func); + +#endif /* CONFIG_NO_WPA_MSG */ #ifdef CONFIG_NO_HOSTAPD_LOGGER #define hostapd_logger(args...) do { } while (0) diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index a150455..cccfcc8 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -117,6 +117,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) WPA_PUT_LE16(pos, data); } +static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); diff --git a/src/wps/http_client.c b/src/wps/http_client.c index fea2a04..9b53b80 100644 --- a/src/wps/http_client.c +++ b/src/wps/http_client.c @@ -21,7 +21,7 @@ #include "http_client.h" -#define HTTP_CLIENT_TIMEOUT 30 +#define HTTP_CLIENT_TIMEOUT_SEC 30 struct http_client { @@ -42,7 +42,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 +52,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 +125,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 +184,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/src/wps/upnp_xml.c b/src/wps/upnp_xml.c index b1b1e2b..a9958ee 100644 --- a/src/wps/upnp_xml.c +++ b/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/src/wps/upnp_xml.h b/src/wps/upnp_xml.h index 62dbe60..616af3d 100644 --- a/src/wps/upnp_xml.h +++ b/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/src/wps/wps.c b/src/wps/wps.c index 619af15..2ba3d4b 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -21,6 +21,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 @@ -46,7 +52,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) } if (cfg->pin) { data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ? - DEV_PW_DEFAULT : data->wps->oob_dev_pw_id; + cfg->dev_pw_id : data->wps->oob_dev_pw_id; data->dev_password = os_malloc(cfg->pin_len); if (data->dev_password == NULL) { os_free(data); @@ -61,12 +67,11 @@ struct wps_data * wps_init(const struct wps_config *cfg) /* 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; } @@ -103,8 +108,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; } @@ -201,19 +209,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 +230,112 @@ 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: 1 if address is authorized, 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 || + 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 +354,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 +389,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 +423,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; } @@ -327,58 +441,60 @@ struct wpabuf * wps_build_assoc_resp_ie(void) * @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, 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)) { + DEV_PW_DEFAULT) || +#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/src/wps/wps.h b/src/wps/wps.h index 1fd1e52..389be3e 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -63,6 +63,13 @@ struct wps_credential { #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 +80,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,8 +94,15 @@ 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[MAX_WPS_VENDOR_EXTENSIONS]; + + int p2p; }; struct oob_conf_data { @@ -156,6 +173,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 +235,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, 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); /** @@ -340,6 +387,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 +447,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 +495,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 +533,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; }; /** @@ -695,16 +781,26 @@ 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); 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_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); @@ -718,7 +814,8 @@ int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, int registrar); 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,6 +823,10 @@ 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); 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, @@ -733,4 +834,150 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); u16 wps_config_methods_str2bin(const char *str); +#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/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 9da556a..d2ca31a 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -19,6 +19,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" @@ -47,6 +49,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 +160,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 +255,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 +333,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); @@ -299,7 +366,8 @@ int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) } wps->oob_dev_pw_id |= 0x0010; - if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) { + if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < + 0) { wpa_printf(MSG_ERROR, "WPS: OOB device password " "generation error"); return -1; @@ -320,3 +388,35 @@ int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) 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/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 30b0e79..55b5573 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -17,7 +17,122 @@ #include "common.h" #include "wps_i.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, @@ -399,6 +514,35 @@ 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; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " "len=%u", type, len); @@ -413,6 +557,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 +577,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 +624,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/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c index 4751bbc..07e087d 100644 --- a/src/wps/wps_attr_process.c +++ b/src/wps/wps_attr_process.c @@ -264,11 +264,18 @@ 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_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 +284,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; } @@ -303,9 +312,7 @@ int wps_process_cred(struct wps_parse_attr *attr, wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled)) return -1; - wps_workaround_cred_key(cred); - - return 0; + return wps_workaround_cred_key(cred); } @@ -324,7 +331,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/src/wps/wps_common.c b/src/wps/wps_common.c index 6ef14db..5d0508c 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -20,6 +20,7 @@ #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" @@ -81,6 +82,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 +244,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 +256,8 @@ unsigned int wps_generate_pin(void) } -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) { union wps_event_data data; @@ -262,6 +266,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); } @@ -325,7 +331,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; } @@ -356,7 +364,8 @@ static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) } if (wps_build_version(data) || - wps_build_oob_dev_password(data, wps)) { + wps_build_oob_dev_password(data, wps) || + wps_build_wfa_ext(data, 0, NULL, 0)) { wpa_printf(MSG_ERROR, "WPS: Build OOB device password " "attribute error"); wpabuf_free(data); @@ -604,6 +613,9 @@ 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_WPS2 + methods |= WPS_CONFIG_VIRT_DISPLAY; +#endif /* CONFIG_WPS2 */ #ifdef CONFIG_WPS_UFD methods |= WPS_CONFIG_USBA; #endif /* CONFIG_WPS_UFD */ @@ -629,7 +641,64 @@ 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; +} diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index 750ca41..43311f3 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -15,7 +15,21 @@ #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 @@ -124,7 +138,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 +224,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 +246,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 +331,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/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 090bfa2..f2fb03a 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -19,71 +19,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 +98,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 +126,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; } @@ -176,6 +219,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/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index a9c16ea..f26a05b 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -17,11 +17,18 @@ 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_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 +36,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/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index dff24d4..7e58291 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -17,6 +17,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 +54,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 +120,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 +132,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 +161,15 @@ 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)) { wpabuf_free(msg); return NULL; } @@ -176,6 +199,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 +232,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 +257,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 +362,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 +398,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 +414,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) { @@ -657,7 +666,7 @@ 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; @@ -682,7 +691,30 @@ 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; @@ -697,9 +729,10 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, 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 +744,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,8 +792,62 @@ 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); cred.cred_attr_len = wpabuf_len(attrs); @@ -779,8 +878,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 +994,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m4_encr(decrypted, attr->version2 != 0) < 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 +1047,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_validate_m6_encr(decrypted, attr->version2 != 0) < 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 +1064,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 +1095,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 +1117,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 != 0) + < 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,12 +1154,6 @@ 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)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); @@ -1025,30 +1162,44 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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 +1235,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; @@ -1130,18 +1275,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; @@ -1180,18 +1320,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 +1374,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/src/wps/wps_er.c b/src/wps/wps_er.c index e0cdd1d..856e9fb 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -62,11 +62,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 +279,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 +414,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 +502,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 */ + 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 +691,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 +761,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 +868,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 +881,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 +1270,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 +1280,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 +1295,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 +1314,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 +1322,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 +1388,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 +1433,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 +1488,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 +1563,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 +1607,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 +1617,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 +1664,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 +1717,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; + + 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, WSC_MSG, msg); + 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 +1747,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 +1906,99 @@ 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; } diff --git a/src/wps/wps_er.h b/src/wps/wps_er.h index b13b950..5388ed1 100644 --- a/src/wps/wps_er.h +++ b/src/wps/wps_er.h @@ -73,6 +73,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 +89,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 +97,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 +107,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/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c index f108435..83de9ad 100644 --- a/src/wps/wps_er_ssdp.c +++ b/src/wps/wps_er_ssdp.c @@ -41,6 +41,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 +113,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 +166,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/src/wps/wps_i.h b/src/wps/wps_i.h index 50e66f6..bdb6da2 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -102,6 +102,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,12 +117,16 @@ 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 *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 */ @@ -162,6 +167,9 @@ struct wps_parse_attr { 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) */ /* variable length fields */ const u8 *manufacturer; @@ -186,12 +194,24 @@ struct wps_parse_attr { 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; /* 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; }; /* wps_common.c */ @@ -202,7 +222,8 @@ 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); @@ -212,6 +233,9 @@ 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; +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); + /* wps_attr_parse.c */ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); @@ -228,6 +252,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); @@ -236,6 +262,7 @@ 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); +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 +291,12 @@ 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); +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); /* 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); -} - #endif /* WPS_I_H */ diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 81ddf3a..e5e8d28 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -21,13 +21,16 @@ #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 */ struct wps_uuid_pin { struct dl_list list; @@ -39,6 +42,7 @@ struct wps_uuid_pin { #define PIN_EXPIRES BIT(1) int flags; struct os_time expiration; + u8 enrollee_addr[ETH_ALEN]; }; @@ -123,10 +127,16 @@ 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]; }; @@ -136,6 +146,52 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +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) { struct wps_registrar_device *prev; @@ -254,20 +310,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 +340,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 +428,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 +467,55 @@ 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) + *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) + *methods |= WPS_CONFIG_PHY_PUSHBUTTON; + if (!(*methods & (WPS_CONFIG_VIRT_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 +536,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 +551,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; } @@ -468,6 +613,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); @@ -499,20 +645,24 @@ void wps_registrar_deinit(struct wps_registrar *reg) /** * 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 @@ -539,6 +689,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 +704,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 +732,32 @@ 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() + * Returns: 0 on success, -1 if not wildcard PIN is enabled + */ +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin, *prev; + + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { + 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 +776,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; } } @@ -673,6 +867,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 +887,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 +945,36 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) } +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e) +{ + if (registrar->pbc) { + wps_registrar_remove_pbc_session(registrar, + uuid_e, NULL); + wps_registrar_pbc_completed(registrar); + } else { + wps_registrar_pin_completed(registrar); + } +} + + +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); + return 1; + } + return 0; +} + + /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request * @reg: Registrar data from wps_registrar_init() @@ -745,7 +986,8 @@ 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; @@ -755,11 +997,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 +1011,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,6 +1038,8 @@ 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); if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { @@ -856,74 +1095,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 +1183,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)) { + 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; @@ -1025,7 +1276,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 +1342,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 +1354,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 +1392,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 +1427,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 +1511,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 +1543,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 +1557,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 +1619,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 +1680,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 +1719,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 +1755,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 +1790,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 +1827,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 +1840,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) { @@ -2047,6 +2337,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) { @@ -2099,14 +2428,19 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, #endif /* CONFIG_WPS_OOB */ 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 +2484,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 +2522,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 +2546,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 +2606,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 +2627,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 +2673,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 +2697,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,15 +2733,10 @@ 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 && @@ -2383,6 +2749,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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 +2765,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 +2815,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; @@ -2506,6 +2877,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 +2887,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; @@ -2559,21 +2925,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 +2971,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; @@ -2687,11 +3052,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, 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); } 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 +3115,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 +3163,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 +3173,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 +3234,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 +3301,43 @@ 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; +} diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index f99b859..06dcd20 100644 --- a/src/wps/wps_upnp.c +++ b/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 + * Copyright (c) 2009-2010, Jouni Malinen * * 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 +#include #include #include #include @@ -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,7 +299,8 @@ 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; @@ -307,20 +314,21 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) struct addrinfo *result = NULL; struct addrinfo *rp; int rerr; - struct subscr_addr *a = NULL; /* 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)); + alloc_len = 2 * (url_len + 1); scratch_mem = os_zalloc(alloc_len); if (scratch_mem == NULL) goto fail; mem = scratch_mem; - strcpy(mem, url); + os_strncpy(mem, url, url_len); + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem); domain_and_port = mem; mem += 1 + os_strlen(mem); delim = os_strchr(domain_and_port, '/'); @@ -332,7 +340,7 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) } domain = mem; strcpy(domain, domain_and_port); - delim = strchr(domain, ':'); + delim = os_strchr(domain, ':'); if (delim) { *delim++ = 0; /* null terminate domain */ if (isdigit(*delim)) @@ -367,6 +375,8 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) goto fail; } 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: " @@ -385,19 +395,17 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) if (path[0] != '/') *mem++ = '/'; strcpy(mem, path); - mem += 1 + strlen(mem); + mem += 1 + os_strlen(mem); 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 +415,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 +426,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 +480,38 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) "\n" "\n"; const char *format_tail = "\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 +531,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); @@ -578,6 +608,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 +636,7 @@ static int subscription_first_event(struct subscription *s) "\n"; const char *tail = "\n"; char txt[10]; + int ret; if (s->sm->wlanevent == NULL) { /* @@ -636,7 +668,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 +678,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 +725,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 +826,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 +955,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 +972,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 +980,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 +1004,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 +1059,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 +1120,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 +1191,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/src/wps/wps_upnp.h b/src/wps/wps_upnp.h index 06bc31f..87b7ab1 100644 --- a/src/wps/wps_upnp.h +++ b/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/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c index 93746da..501ecbc 100644 --- a/src/wps/wps_upnp_ap.c +++ b/src/wps/wps_upnp_ap.c @@ -39,18 +39,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); + 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,6 +59,19 @@ 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); } diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index ae5efdb..2c8ed4f 100644 --- a/src/wps/wps_upnp_event.c +++ b/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 + * Copyright (c) 2009-2010, Jouni Malinen * * 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/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index b31875a..3ecf05d 100644 --- a/src/wps/wps_upnp_i.h +++ b/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); diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 8505d05..4c4aebf 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/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; @@ -875,11 +871,17 @@ int ssdp_open_multicast_sock(u32 ip_addr) #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)); 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)); return -1; + } #if 0 /* not needed, because we don't receive using multicast_sd */ { diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index 9a6b36e..ce0bede 100644 --- a/src/wps/wps_upnp_web.c +++ b/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, "\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/src/wps/wps_validate.c b/src/wps/wps_validate.c new file mode 100644 index 0000000..c3071a0 --- /dev/null +++ b/src/wps/wps_validate.c @@ -0,0 +1,1981 @@ +/* + * Wi-Fi Protected Setup - Strict protocol validation routines + * Copyright (c) 2010, Atheros Communications, Inc. + * + * 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 "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; +} diff --git a/wpa_supplicant/.gitignore b/wpa_supplicant/.gitignore index e7e034c..0e3ad1b 100644 --- a/wpa_supplicant/.gitignore +++ b/wpa_supplicant/.gitignore @@ -1,8 +1 @@ -*.d -.config -eapol_test -preauth_test -wpa_cli -wpa_passphrase -wpa_supplicant -wpa_priv +*.service diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk new file mode 100644 index 0000000..40fde34 --- /dev/null +++ b/wpa_supplicant/Android.mk @@ -0,0 +1,1463 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +LOCAL_PATH := $(call my-dir) +PKG_CONFIG ?= pkg-config + +WPA_BUILD_SUPPLICANT := false +ifneq ($(TARGET_SIMULATOR),true) + ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + WPA_BUILD_SUPPLICANT := true + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y + endif +endif + +include $(LOCAL_PATH)/.config + +# To ignore possible wrong network configurations +L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS + +# Set Android log name +L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\" + +# Use Android specific directory for control interface sockets +L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" +L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\" + +# To force sizeof(enum) = 4 +ifeq ($(TARGET_ARCH),arm) +L_CFLAGS += -mabi=aapcs-linux +endif + +# To allow non-ASCII characters in SSID +L_CFLAGS += -DWPA_UNICODE_SSID + +# OpenSSL is configured without engines on Android +L_CFLAGS += -DOPENSSL_NO_ENGINE + +INCLUDES = $(LOCAL_PATH) +INCLUDES += $(LOCAL_PATH)/src +INCLUDES += $(LOCAL_PATH)/src/common +# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes +INCLUDES += $(LOCAL_PATH)/src/drivers +INCLUDES += $(LOCAL_PATH)/src/eap_common +INCLUDES += $(LOCAL_PATH)/src/eapol_supp +INCLUDES += $(LOCAL_PATH)/src/eap_peer +INCLUDES += $(LOCAL_PATH)/src/eap_server +INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw +INCLUDES += $(LOCAL_PATH)/src/l2_packet +INCLUDES += $(LOCAL_PATH)/src/radius +INCLUDES += $(LOCAL_PATH)/src/rsn_supp +INCLUDES += $(LOCAL_PATH)/src/tls +INCLUDES += $(LOCAL_PATH)/src/utils +INCLUDES += $(LOCAL_PATH)/src/wps +INCLUDES += external/openssl/include +INCLUDES += frameworks/base/cmds/keystore +ifdef CONFIG_DRIVER_NL80211 +INCLUDES += external/libnl-headers +endif + +OBJS = config.c +OBJS += notify.c +OBJS += bss.c +OBJS += eap_register.c +OBJS += src/utils/common.c +OBJS += src/utils/wpa_debug.c +OBJS += src/utils/wpabuf.c +OBJS_p = wpa_passphrase.c +OBJS_p += src/utils/common.c +OBJS_p += src/utils/wpa_debug.c +OBJS_p += src/utils/wpabuf.c +OBJS_c = wpa_cli.c src/common/wpa_ctrl.c +OBJS_c += src/utils/wpa_debug.c +OBJS_c += src/utils/common.c +OBJS_d = +OBJS_priv = + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +L_CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += src/utils/os_$(CONFIG_OS).c +OBJS_p += src/utils/os_$(CONFIG_OS).c +OBJS_c += src/utils/os_$(CONFIG_OS).c + +ifdef CONFIG_WPA_TRACE +L_CFLAGS += -DWPA_TRACE +OBJS += src/utils/trace.c +OBJS_p += src/utils/trace.c +OBJS_c += src/utils/trace.c +LDFLAGS += -rdynamic +L_CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += src/utils/$(CONFIG_ELOOP).c +OBJS_c += src/utils/$(CONFIG_ELOOP).c + + +ifdef CONFIG_EAPOL_TEST +L_CFLAGS += -Werror -DEAPOL_TEST +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.c +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +L_CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.c +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.c +endif + +ifdef CONFIG_NO_CONFIG_WRITE +L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_IEEE80211W +L_CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R +OBJS += src/rsn_supp/wpa_ft.c +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS +L_CFLAGS += -DCONFIG_TDLS +OBJS += src/rsn_supp/tdls.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +L_CFLAGS += -DCONFIG_TDLS_TESTING +endif + +ifdef CONFIG_PEERKEY +L_CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += src/rsn_supp/wpa.c +OBJS += src/rsn_supp/preauth.c +OBJS += src/rsn_supp/pmksa_cache.c +OBJS += src/rsn_supp/peerkey.c +OBJS += src/rsn_supp/wpa_ie.c +OBJS += src/common/wpa_common.c +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_IBSS_RSN +OBJS += ibss_rsn.c +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.c +OBJS += src/p2p/p2p.c +OBJS += src/p2p/p2p_utils.c +OBJS += src/p2p/p2p_parse.c +OBJS += src/p2p/p2p_build.c +OBJS += src/p2p/p2p_go_neg.c +OBJS += src/p2p/p2p_sd.c +OBJS += src/p2p/p2p_pd.c +OBJS += src/p2p/p2p_invitation.c +OBJS += src/p2p/p2p_dev_disc.c +OBJS += src/p2p/p2p_group.c +OBJS += src/ap/p2p_hostapd.c +L_CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +NEED_80211_COMMON=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +L_CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.c +L_CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +ifdef CONFIG_NO_WPA2 +L_CFLAGS += -DCONFIG_NO_WPA2 +endif + +include $(LOCAL_PATH)/src/drivers/drivers.mk + +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +L_CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +L_CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +L_CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +L_CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += src/eap_peer/eap_tls.so +else +L_CFLAGS += -DEAP_TLS +OBJS += src/eap_peer/eap_tls.c +OBJS_h += src/eap_server/eap_server_tls.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +L_CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_peap.so +else +L_CFLAGS += -DEAP_PEAP +OBJS += src/eap_peer/eap_peap.c +OBJS += src/eap_common/eap_peap_common.c +OBJS_h += src/eap_server/eap_server_peap.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +L_CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += src/eap_peer/eap_ttls.so +else +L_CFLAGS += -DEAP_TTLS +OBJS += src/eap_peer/eap_ttls.c +OBJS_h += src/eap_server/eap_server_ttls.c +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +L_CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += src/eap_peer/eap_md5.so +else +L_CFLAGS += -DEAP_MD5 +OBJS += src/eap_peer/eap_md5.c +OBJS_h += src/eap_server/eap_server_md5.c +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += src/eap_peer/eap_mschapv2.so +EAPDYN += src/eap_peer/mschapv2.so +else +L_CFLAGS += -DEAP_MSCHAPv2 +OBJS += src/eap_peer/eap_mschapv2.c +OBJS += src/eap_peer/mschapv2.c +OBJS_h += src/eap_server/eap_server_mschapv2.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +L_CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += src/eap_peer/eap_gtc.so +else +L_CFLAGS += -DEAP_GTC +OBJS += src/eap_peer/eap_gtc.c +OBJS_h += src/eap_server/eap_server_gtc.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +L_CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += src/eap_peer/eap_otp.so +else +L_CFLAGS += -DEAP_OTP +OBJS += src/eap_peer/eap_otp.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +L_CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += src/eap_peer/eap_sim.so +else +L_CFLAGS += -DEAP_SIM +OBJS += src/eap_peer/eap_sim.c +OBJS_h += src/eap_server/eap_server_sim.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +L_CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_leap.so +else +L_CFLAGS += -DEAP_LEAP +OBJS += src/eap_peer/eap_leap.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +L_CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += src/eap_peer/eap_psk.so +else +L_CFLAGS += -DEAP_PSK +OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c +OBJS_h += src/eap_server/eap_server_psk.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +L_CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += src/eap_peer/eap_aka.so +else +L_CFLAGS += -DEAP_AKA +OBJS += src/eap_peer/eap_aka.c +OBJS_h += src/eap_server/eap_server_aka.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +L_CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += src/eap_common/eap_sim_common.c +OBJS_h += src/eap_server/eap_sim_db.c +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +L_CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += src/eap_peer/eap_fast.so +EAPDYN += src/eap_common/eap_fast_common.c +else +L_CFLAGS += -DEAP_FAST +OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c +OBJS += src/eap_common/eap_fast_common.c +OBJS_h += src/eap_server/eap_server_fast.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +L_CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += src/eap_peer/eap_pax.so +else +L_CFLAGS += -DEAP_PAX +OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c +OBJS_h += src/eap_server/eap_server_pax.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +L_CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += src/eap_peer/eap_sake.so +else +L_CFLAGS += -DEAP_SAKE +OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c +OBJS_h += src/eap_server/eap_server_sake.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +L_CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += src/eap_peer/eap_gpsk.so +else +L_CFLAGS += -DEAP_GPSK +OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c +OBJS_h += src/eap_server/eap_server_gpsk.c +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +L_CFLAGS += -DEAP_PWD +OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c +OBJS_h += src/eap_server/eap_pwd.c +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +L_CFLAGS += -DCONFIG_WPS2 +endif + +# EAP-WSC +L_CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.c +OBJS += src/utils/uuid.c +OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c +OBJS += src/wps/wps.c +OBJS += src/wps/wps_common.c +OBJS += src/wps/wps_attr_parse.c +OBJS += src/wps/wps_attr_build.c +OBJS += src/wps/wps_attr_process.c +OBJS += src/wps/wps_dev_attr.c +OBJS += src/wps/wps_enrollee.c +OBJS += src/wps/wps_registrar.c +OBJS_h += src/eap_server/eap_server_wsc.c +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_80211_COMMON=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_UFD +L_CFLAGS += -DCONFIG_WPS_UFD +OBJS += src/wps/wps_ufd.c +NEED_WPS_OOB=y +endif + +ifdef CONFIG_WPS_NFC +L_CFLAGS += -DCONFIG_WPS_NFC +OBJS += src/wps/ndef.c +OBJS += src/wps/wps_nfc.c +NEED_WPS_OOB=y +ifdef CONFIG_WPS_NFC_PN531 +PN531_PATH ?= /usr/local/src/nfc +L_CFLAGS += -DCONFIG_WPS_NFC_PN531 +L_CFLAGS += -I${PN531_PATH}/inc +OBJS += src/wps/wps_nfc_pn531.c +LIBS += ${PN531_PATH}/lib/wpsnfc.dll +LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll +endif +endif + +ifdef NEED_WPS_OOB +L_CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +L_CFLAGS += -DCONFIG_WPS_ER +OBJS += src/wps/wps_er.c +OBJS += src/wps/wps_er_ssdp.c +endif + +ifdef CONFIG_WPS_UPNP +L_CFLAGS += -DCONFIG_WPS_UPNP +OBJS += src/wps/wps_upnp.c +OBJS += src/wps/wps_upnp_ssdp.c +OBJS += src/wps/wps_upnp_web.c +OBJS += src/wps/wps_upnp_event.c +OBJS += src/wps/wps_upnp_ap.c +OBJS += src/wps/upnp_xml.c +OBJS += src/wps/httpread.c +OBJS += src/wps/http_client.c +OBJS += src/wps/http_server.c +endif + +ifdef CONFIG_WPS_STRICT +L_CFLAGS += -DCONFIG_WPS_STRICT +OBJS += src/wps/wps_validate.c +endif + +ifdef CONFIG_WPS_TESTING +L_CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +L_CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c +EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +else +L_CFLAGS += -DEAP_IKEV2 +OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c +OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +OBJS_h += src/eap_server/eap_server_ikev2.c +OBJS_h += src/eap_server/ikev2.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += src/eap_peer/eap_vendor_test.so +else +L_CFLAGS += -DEAP_VENDOR_TEST +OBJS += src/eap_peer/eap_vendor_test.c +OBJS_h += src/eap_server/eap_server_vendor_test.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +L_CFLAGS += -DEAP_TNC +OBJS += src/eap_peer/eap_tnc.c +OBJS += src/eap_peer/tncc.c +OBJS_h += src/eap_server/eap_server_tnc.c +OBJS_h += src/eap_server/tncs.c +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +L_CFLAGS += -DIEEE8021X_EAPOL +OBJS += src/eapol_supp/eapol_supp_sm.c +OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_80211_COMMON=y +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_AP +OBJS += ap.c +L_CFLAGS += -DCONFIG_NO_RADIUS +L_CFLAGS += -DCONFIG_NO_ACCOUNTING +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += src/ap/hostapd.c +OBJS += src/ap/wpa_auth_glue.c +OBJS += src/ap/utils.c +OBJS += src/ap/authsrv.c +OBJS += src/ap/ap_config.c +OBJS += src/utils/ip_addr.c +OBJS += src/ap/sta_info.c +OBJS += src/ap/tkip_countermeasures.c +OBJS += src/ap/ap_mlme.c +OBJS += src/ap/ieee802_1x.c +OBJS += src/eapol_auth/eapol_auth_sm.c +OBJS += src/ap/ieee802_11_auth.c +OBJS += src/ap/ieee802_11_shared.c +OBJS += src/ap/drv_callbacks.c +OBJS += src/ap/ap_drv_ops.c +OBJS += src/ap/beacon.c +ifdef CONFIG_IEEE80211N +OBJS += src/ap/ieee802_11_ht.c +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/ap/ctrl_iface_ap.c +endif + +L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += src/eap_server/eap_server.c +OBJS += src/eap_server/eap_server_identity.c +OBJS += src/eap_server/eap_server_methods.c + +ifdef CONFIG_IEEE80211N +L_CFLAGS += -DCONFIG_IEEE80211N +endif + +ifdef NEED_AP_MLME +OBJS += src/ap/wmm.c +OBJS += src/ap/ap_list.c +OBJS += src/ap/ieee802_11.c +OBJS += src/ap/hw_features.c +L_CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +L_CFLAGS += -DEAP_SERVER_WSC +OBJS += src/ap/wps_hostapd.c +OBJS += src/eap_server/eap_server_wsc.c +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +L_CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += src/ap/wpa_auth.c +OBJS += src/ap/wpa_auth_ie.c +OBJS += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_EAP_SERVER +L_CFLAGS += -DEAP_SERVER +OBJS_h += src/eap_server/eap_server.c +OBJS_h += src/eap_server/eap_server_identity.c +OBJS_h += src/eap_server/eap_server_methods.c +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += src/utils/ip_addr.c +OBJS_h += src/radius/radius.c +OBJS_h += src/radius/radius_client.c +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += src/eapol_auth/eapol_auth_sm.c +OBJS_h += src/ap/ieee802_1x.c +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += src/ap/wpa_auth.c +OBJS_h += src/ap/wpa_auth_ie.c +OBJS_h += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS_h += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS_h += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += src/utils/pcsc_funcs.c +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +L_CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +L_CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += src/crypto/milenage.c +NEED_AES_ENCBLOCK=y +endif + +ifdef CONFIG_PKCS12 +L_CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +L_CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += src/crypto/ms_funcs.c +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += src/eap_common/chap.c +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += src/eap_peer/eap_tls_common.c +OBJS_h += src/eap_server/eap_server_tls_common.c +NEED_TLS_PRF=y +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifdef CONFIG_TLSV11 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +L_CFLAGS += -DEAP_TLS_OPENSSL +OBJS += src/crypto/tls_openssl.c +LIBS += -lssl +endif +OBJS += src/crypto/crypto_openssl.c +OBJS_p += src/crypto/crypto_openssl.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_openssl.c +endif +LIBS += -lcrypto +LIBS_p += -lcrypto +endif + +ifeq ($(CONFIG_TLS), gnutls) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_gnutls.c +LIBS += -lgnutls -lgpg-error +endif +OBJS += src/crypto/crypto_gnutls.c +OBJS_p += src/crypto/crypto_gnutls.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_gnutls.c +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), schannel) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_schannel.c +endif +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_cryptoapi.c +endif +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), nss) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_nss.c +LIBS += -lssl3 +endif +OBJS += src/crypto/crypto_nss.c +OBJS_p += src/crypto/crypto_nss.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_nss.c +endif +LIBS += -lnss3 +LIBS_p += -lnss3 +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += src/crypto/crypto_internal-rsa.c +OBJS += src/crypto/tls_internal.c +OBJS += src/tls/tlsv1_common.c +OBJS += src/tls/tlsv1_record.c +OBJS += src/tls/tlsv1_cred.c +OBJS += src/tls/tlsv1_client.c +OBJS += src/tls/tlsv1_client_write.c +OBJS += src/tls/tlsv1_client_read.c +OBJS += src/tls/asn1.c +OBJS += src/tls/rsa.c +OBJS += src/tls/x509v3.c +OBJS += src/tls/pkcs1.c +OBJS += src/tls/pkcs5.c +OBJS += src/tls/pkcs8.c +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +NEED_MODEXP=y +NEED_CIPHER=y +L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += src/crypto/crypto_internal-cipher.c +endif +ifdef NEED_MODEXP +OBJS += src/crypto/crypto_internal-modexp.c +OBJS += src/tls/bignum.c +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += src/crypto/crypto_libtomcrypt.c +OBJS_p += src/crypto/crypto_libtomcrypt.c +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += src/crypto/crypto_internal.c +OBJS_p += src/crypto/crypto_internal.c +NEED_AES_ENC=y +L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +L_CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_none.c +L_CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += src/crypto/crypto_none.c +OBJS_p += src/crypto/crypto_none.c +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += src/crypto/tls_none.c +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c +endif + +AESOBJS += src/crypto/aes-unwrap.c +ifdef NEED_AES_EAX +AESOBJS += src/crypto/aes-eax.c +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += src/crypto/aes-ctr.c +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += src/crypto/aes-encblock.c +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-omac1.c +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-wrap.c +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-cbc.c +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal-enc.c +endif +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +SHA1OBJS = +ifdef NEED_SHA1 +SHA1OBJS += src/crypto/sha1.c +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += src/crypto/sha1-internal.c +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += src/crypto/fips_prf_internal.c +endif +endif +ifdef CONFIG_NO_WPA_PASSPHRASE +L_CFLAGS += -DCONFIG_NO_PBKDF2 +else +SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif +ifdef NEED_T_PRF +SHA1OBJS += src/crypto/sha1-tprf.c +endif +ifdef NEED_TLS_PRF +SHA1OBJS += src/crypto/sha1-tlsprf.c +endif +endif + +MD5OBJS = src/crypto/md5.c +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += src/crypto/md5-internal.c +endif +ifdef CONFIG_FIPS +MD5OBJS += src/crypto/md5-non-fips.c +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += src/crypto/md4-internal.c +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += src/crypto/des-internal.c +endif +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +OBJS += src/crypto/rc4.c +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +SHA256OBJS += src/crypto/sha256.c +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += src/crypto/sha256-internal.c +endif +OBJS += $(SHA256OBJS) +endif + +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_groups.c +endif +ifdef NEED_DH_GROUPS_ALL +L_CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_group5.c +endif +endif + +ifdef CONFIG_NO_RANDOM_POOL +L_CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += src/crypto/random.c +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +L_CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.c +endif +DBUS_OBJS += dbus/dbus_dict_helpers.c +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.c +DBUS_OBJS += dbus/dbus_new_helpers.c +DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.c +endif +ifdef CONFIG_P2P +DBUS_OBJS += dbus/dbus_new_handlers_p2p.c +endif +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.c +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.c +endif + +OBJS += $(DBUS_OBJS) +L_CFLAGS += $(DBUS_CFLAGS) +LIBS += $(DBUS_LIBS) + +ifdef CONFIG_READLINE +OBJS_c += src/utils/edit_readline.c +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += src/utils/edit.c +else +OBJS_c += src/utils/edit_simple.c +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +L_CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +L_CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +L_CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += src/utils/base64.c +endif + +ifdef NEED_SME +NEED_80211_COMMON=y +OBJS += sme.c +L_CFLAGS += -DCONFIG_SME +endif + +ifdef NEED_80211_COMMON +OBJS += src/common/ieee802_11_common.c +endif + +ifdef NEED_EAP_COMMON +OBJS += src/eap_common/eap_common.c +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_FILE +L_CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +L_CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.c +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +L_CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.c +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +L_CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.c +endif + +ifdef NEED_GAS +OBJS += ../src/common/gas.c +OBJS += gas_query.c +L_CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.c +L_CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += src/drivers/driver_common.c + +OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.c +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c +OBJS_t += src/radius/radius_client.c +OBJS_t += src/radius/radius.c +ifndef CONFIG_AP +OBJS_t += src/utils/ip_addr.c +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c +OBJS += $(CONFIG_MAIN).c + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) src/drivers/drivers.c +OBJS_priv += $(OBJS_l2) +OBJS_priv += src/utils/os_$(CONFIG_OS).c +OBJS_priv += src/utils/$(CONFIG_ELOOP).c +OBJS_priv += src/utils/common.c +OBJS_priv += src/utils/wpa_debug.c +OBJS_priv += src/utils/wpabuf.c +OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_TEST +OBJS_priv += $(SHA1OBJS) +OBJS_priv += $(MD5OBJS) +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += src/crypto/crypto_openssl.c +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += src/crypto/crypto_gnutls.c +endif +ifeq ($(CONFIG_TLS), nss) +OBJS_priv += src/crypto/crypto_nss.c +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += src/crypto/crypto_libtomcrypt.c +else +OBJS_priv += src/crypto/crypto_internal.c +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += src/l2_packet/l2_packet_privsep.c +OBJS += src/drivers/driver_privsep.c +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) src/drivers/drivers.c +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += src/drivers/ndis_events.c +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +ifeq ($(WPA_BUILD_SUPPLICANT),true) + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_cli +LOCAL_MODULE_TAGS := debug +LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS_c) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_supplicant +ifdef CONFIG_DRIVER_CUSTOM +LOCAL_STATIC_LIBRARIES := libCustomWifi +endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) +endif +LOCAL_SHARED_LIBRARIES := libc libcutils +ifeq ($(CONFIG_TLS), openssl) +LOCAL_SHARED_LIBRARIES += libcrypto libssl +endif +ifdef CONFIG_DRIVER_NL80211 +LOCAL_STATIC_LIBRARIES += libnl_2 +endif +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := eapol_test +#ifdef CONFIG_DRIVER_CUSTOM +#LOCAL_STATIC_LIBRARIES := libCustomWifi +#endif +#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl +#LOCAL_CFLAGS := $(L_CFLAGS) +#LOCAL_SRC_FILES := $(OBJS_t) +#LOCAL_C_INCLUDES := $(INCLUDES) +#include $(BUILD_EXECUTABLE) +# +######################## +# +#local_target_dir := $(TARGET_OUT)/etc/wifi +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := wpa_supplicant.conf +#LOCAL_MODULE_TAGS := user +#LOCAL_MODULE_CLASS := ETC +#LOCAL_MODULE_PATH := $(local_target_dir) +#LOCAL_SRC_FILES := $(LOCAL_MODULE) +#include $(BUILD_PREBUILT) +# +######################## + +endif # ifeq ($(WPA_BUILD_SUPPLICANT),true) + +include $(CLEAR_VARS) +LOCAL_MODULE = libwpa_client +LOCAL_CFLAGS = $(L_CFLAGS) +LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c +LOCAL_C_INCLUDES = $(INCLUDES) +LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_COPY_HEADERS_TO := libwpa_client +LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h +include $(BUILD_SHARED_LIBRARY) diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index 56046c3..a51298b 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,23 +1,178 @@ ChangeLog for wpa_supplicant -2010-09-07 - v0.7.3 - * fixed fallback from failed PMKSA caching into full EAP authentication - [Bug 355] - * fixed issue with early D-Bus signals during initialization - * fixed X.509 name handling in internal TLS - * fixed WPS ER to use corrent Enrollee MAC Address in Credential - * fixed scanning routines ot improve AP selection for WPS - * added WPS workaround for open networks - * fixed WPS Diffie-Hellman derivation to use correct public key length - * fixed wpa_supplicant AP mode operations to ignore Supplicant and - scan result events - * improved SME operations with nl80211 - * fixed WPS ER event_id handling in some cases - * fixed some issues with bgscan simple to avoid unnecessary scans - * fixed issue with l2_packet_ndis overlapped writes corrupting stack - [Bug 328] - * updated WinPcap to the latest stable version 4.1.2 in Windows - installer +2012-04-18 - v1.0 + * bsd: Add support for setting HT values in IFM_MMASK. + * Delay STA entry removal until Deauth/Disassoc TX status in AP mode. + This allows the driver to use PS buffering of Deauthentication and + Disassociation frames when the STA is in power save sleep. Only + available with drivers that provide TX status events for Deauth/ + Disassoc frames (nl80211). + * Drop oldest unknown BSS table entries first. This makes it less + likely to hit connection issues in environments with huge number + of visible APs. + * Add systemd support. + * Add support for setting the syslog facility from the config file + at build time. + * atheros: Add support for IEEE 802.11w configuration. + * AP mode: Allow enable HT20 if driver supports it, by setting the + config parameter ieee80211n. + * Allow AP mode to disconnect STAs based on low ACK condition (when + the data connection is not working properly, e.g., due to the STA + going outside the range of the AP). Disabled by default, enable by + config option disassoc_low_ack. + * nl80211: + - Support GTK rekey offload. + - Support PMKSA candidate events. This adds support for RSN + pre-authentication with nl80211 interface and drivers that handle + roaming internally. + * dbus: + - Add a DBus signal for EAP SM requests, emitted on the Interface + object. + - Export max scan ssids supported by the driver as MaxScanSSID. + - Add signal Certification for information about server certification. + - Add BSSExpireAge and BSSExpireCount interface properties and + support set/get, which allows for setting BSS cache expiration age + and expiration scan count. + - Add ConfigFile to AddInterface properties. + - Add Interface.Country property and support to get/set the value. + - Add DBus property CurrentAuthMode. + - P2P DBus API added. + - Emit property changed events (for property BSSs) when adding/ + removing BSSs. + - Treat '' in SSIDs of Interface.Scan as a request for broadcast + scan, instead of ignoring it. + - Add DBus getter/setter for FastReauth. + - Raise PropertiesChanged on org.freedesktop.DBus.Properties. + * wpa_cli: + - Send AP-STA-DISCONNECTED event when an AP disconnects a station + due to inactivity. + - Make second argument to set command optional. This can be used to + indicate a zero length value. + - Add signal_poll command. + - Add bss_expire_age and bss_expire_count commands to set/get BSS + cache expiration age and expiration scan count. + - Add ability to set scan interval (the time in seconds wpa_s waits + before requesting a new scan after failing to find a suitable + network in scan results) using scan_interval command. + - Add event CTRL-EVENT-ASSOC-REJECT for association rejected. + - Add command get version, that returns wpa_supplicant version string. + - Add command sta_autoconnect for disabling automatic reconnection + on receiving disconnection event. + - Setting bssid parameter to an empty string "" or any can now be + used to clear the bssid_set flag in a network block, i.e., to remove + bssid filtering. + - Add tdls_testing command to add a special testing feature for + changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be + enabled as well. + - For interworking, add wpa_cli commands interworking_select, + interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp. + - Many P2P commands were added. See README-P2P. + - Many WPS/WPS ER commands - see WPS/WPS ER sections for details. + - Allow set command to change global config parameters. + - Add log_level command, which can be used to display the current + debugging level and to change the log level during run time. + - Add note command, which can be used to insert notes to the debug + log. + - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y + can now be used to build wpa_cli with internal implementation of + line editing and history support. This can be used as a replacement + for CONFIG_READLINE=y. + * AP mode: Add max_num_sta config option, which can be used to limit + the number of stations allowed to connect to the AP. + * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad + config file. + * wext: Increase scan timeout from 5 to 10 seconds. + * Add blacklist command, allowing an external program to + manage the BSS blacklist and display its current contents. + * WPS: + - Add wpa_cli wps_pin get command for generating random PINs. This can + be used in a UI to generate a PIN without starting WPS (or P2P) + operation. + - Set RF bands based on driver capabilities, instead of hardcoding + them. + - Add mechanism for indicating non-standard WPS errors. + - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks + by default. + - Add wps_ap_pin cli command for wpa_supplicant AP mode. + - Add wps_check_pin cli command for processing PIN from user input. + UIs can use this command to process a PIN entered by a user and to + validate the checksum digit (if present). + - Cancel WPS operation on PBC session overlap detection. + - New wps_cancel command in wpa_cli will cancel a pending WPS + operation. + - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers. + - Trigger WPS config update on Manufacturer, Model Name, Model + Number, and Serial Number changes. + - Fragment size is now configurable for EAP-WSC peer. Use + wpa_cli set wps_fragment_size . + - Disable AP PIN after 10 consecutive failures. Slow down attacks on + failures up to 10. + - Allow AP to start in Enrollee mode without AP PIN for probing, to + be compatible with Windows 7. + - Add Config Error into WPS-FAIL events to provide more info to the + user on how to resolve the issue. + - Label and Display config methods are not allowed to be enabled + at the same time, since it is unclear which PIN to use if both + methods are advertised. + - When controlling multiple interfaces: + - apply WPS commands to all interfaces configured to use WPS + - apply WPS config changes to all interfaces that use WPS + - when an attack is detected on any interface, disable AP PIN on + all interfaces + * WPS ER: + - Add special AP Setup Locked mode to allow read only ER. + ap_setup_locked=2 can now be used to enable a special mode where + WPS ER can learn the current AP settings, but cannot change them. + - Show SetSelectedRegistrar events as ctrl_iface events + - Add wps_er_set_config to enroll a network based on a local + network configuration block instead of having to (re-)learn the + current AP settings with wps_er_learn. + - Allow AP filtering based on IP address, add ctrl_iface event for + learned AP settings, add wps_er_config command to configure an AP. + * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2) + - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool + for testing protocol extensibility. + - Add build option CONFIG_WPS_STRICT to allow disabling of WPS + workarounds. + - Add support for AuthorizedMACs attribute. + * TDLS: + - Propogate TDLS related nl80211 capability flags from kernel and + add them as driver capability flags. If the driver doesn't support + capabilities, assume TDLS is supported internally. When TDLS is + explicitly not supported, disable all user facing TDLS operations. + - Allow TDLS to be disabled at runtime (mostly for testing). + Use set tdls_disabled. + - Honor AP TDLS settings that prohibit/allow TDLS. + - Add a special testing feature for changing TDLS behavior. Use + CONFIG_TDLS_TESTING build param to enable. Configure at runtime + with tdls_testing cli command. + - Add support for TDLS 802.11z. + * wlantest: Add a tool wlantest for IEEE802.11 protocol testing. + wlantest can be used to capture frames from a monitor interface + for realtime capturing or from pcap files for offline analysis. + * Interworking: Support added for 802.11u. Enable in .config with + CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters + for interworking. wpa_cli commands added to support this are + interworking_select, interworking_connect, anqp_get, fetch_anqp, + and stop_fetch_anqp. + * Android: Add build and runtime support for Android wpa_supplicant. + * bgscan learn: Add new bgscan that learns BSS information based on + previous scans, and uses that information to dynamically generate + the list of channels for background scans. + * Add a new debug message level for excessive information. Use + -ddd to enable. + * TLS: Add support for tls_disable_time_checks=1 in client mode. + * Internal TLS: + - Add support for TLS v1.1 (RFC 4346). Enable with build parameter + CONFIG_TLSV11. + - Add domainComponent parser for X.509 names. + * Linux: Add RFKill support by adding an interface state "disabled". + * Reorder some IEs to get closer to IEEE 802.11 standard. Move + WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames. + Move HT IEs to be later in (Re)Assoc Resp. + * Solaris: Add support for wired 802.1X client. + * Wi-Fi Direct support. See README-P2P for more information. + * Many bugfixes. 2010-04-18 - v0.7.2 * nl80211: fixed number of issues with roaming diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 1d25623..22df8a0 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -8,11 +8,27 @@ endif export LIBDIR ?= /usr/local/lib/ export BINDIR ?= /usr/local/sbin/ +PKG_CONFIG ?= pkg-config CFLAGS += -I../src CFLAGS += -I../src/utils -ALL=wpa_supplicant wpa_passphrase wpa_cli +-include .config + +BINALL=wpa_supplicant wpa_cli + +ifndef CONFIG_NO_WPA_PASSPHRASE +BINALL += wpa_passphrase +endif + +ALL = $(BINALL) +ALL += systemd/wpa_supplicant.service +ALL += systemd/wpa_supplicant@.service +ALL += systemd/wpa_supplicant-nl80211@.service +ALL += systemd/wpa_supplicant-wired@.service +ALL += dbus/fi.epitest.hostap.WPASupplicant.service +ALL += dbus/fi.w1.wpa_supplicant1.service + all: verify_config $(ALL) dynamic_eap_methods @@ -33,9 +49,10 @@ mkconfig: echo CONFIG_DRIVER_HOSTAP=y >> .config echo CONFIG_DRIVER_WEXT=y >> .config -install: all - mkdir -p $(DESTDIR)$(BINDIR) - for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done +$(DESTDIR)$(BINDIR)/%: % + install -D $(<) $(@) + +install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL)) $(MAKE) -C ../src install OBJS = config.o @@ -50,8 +67,8 @@ OBJS_p += ../src/utils/common.o OBJS_p += ../src/utils/wpa_debug.o OBJS_p += ../src/utils/wpabuf.o OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o - --include .config +OBJS_c += ../src/utils/wpa_debug.o +OBJS_c += ../src/utils/common.o ifndef CONFIG_OS ifdef CONFIG_NATIVE_WINDOWS @@ -74,7 +91,7 @@ CFLAGS += -DWPA_TRACE OBJS += ../src/utils/trace.o OBJS_p += ../src/utils/trace.o OBJS_c += ../src/utils/trace.o -OBJS_c += ../src/utils/wpa_debug.o +OBJS_priv += ../src/utils/trace.o LDFLAGS += -rdynamic CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD @@ -89,6 +106,7 @@ ifndef CONFIG_ELOOP CONFIG_ELOOP=eloop endif OBJS += ../src/utils/$(CONFIG_ELOOP).o +OBJS_c += ../src/utils/$(CONFIG_ELOOP).o ifdef CONFIG_EAPOL_TEST @@ -141,6 +159,17 @@ NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_TDLS +CFLAGS += -DCONFIG_TDLS +OBJS += ../src/rsn_supp/tdls.o +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +CFLAGS += -DCONFIG_TDLS_TESTING +endif + ifdef CONFIG_PEERKEY CFLAGS += -DCONFIG_PEERKEY endif @@ -166,6 +195,36 @@ CFLAGS += -DCONFIG_IBSS_RSN OBJS += ibss_rsn.o endif +ifdef CONFIG_P2P +OBJS += p2p_supplicant.o +OBJS += ../src/p2p/p2p.o +OBJS += ../src/p2p/p2p_utils.o +OBJS += ../src/p2p/p2p_parse.o +OBJS += ../src/p2p/p2p_build.o +OBJS += ../src/p2p/p2p_go_neg.o +OBJS += ../src/p2p/p2p_sd.o +OBJS += ../src/p2p/p2p_pd.o +OBJS += ../src/p2p/p2p_invitation.o +OBJS += ../src/p2p/p2p_dev_disc.o +OBJS += ../src/p2p/p2p_group.o +OBJS += ../src/ap/p2p_hostapd.o +CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +NEED_80211_COMMON=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.o +CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + ifdef CONFIG_NO_WPA2 CFLAGS += -DCONFIG_NO_WPA2 endif @@ -454,7 +513,19 @@ NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_EAP_PWD +CFLAGS += -DEAP_PWD +OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o +OBJS_h += ../src/eap_server/eap_pwd.o +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +CFLAGS += -DCONFIG_WPS2 +endif + # EAP-WSC CFLAGS += -DCONFIG_WPS -DEAP_WSC OBJS += wps_supplicant.o @@ -522,6 +593,19 @@ OBJS += ../src/wps/http_client.o OBJS += ../src/wps/http_server.o endif +ifdef CONFIG_WPS_STRICT +CFLAGS += -DCONFIG_WPS_STRICT +OBJS += ../src/wps/wps_validate.o +endif + +ifdef CONFIG_WPS_TESTING +CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + endif ifdef CONFIG_EAP_IKEV2 @@ -604,8 +688,13 @@ OBJS += ../src/ap/ap_mlme.o OBJS += ../src/ap/ieee802_1x.o OBJS += ../src/eapol_auth/eapol_auth_sm.o OBJS += ../src/ap/ieee802_11_auth.o +OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/drv_callbacks.o OBJS += ../src/ap/ap_drv_ops.o +OBJS += ../src/ap/beacon.o +ifdef CONFIG_IEEE80211N +OBJS += ../src/ap/ieee802_11_ht.o +endif ifdef CONFIG_CTRL_IFACE OBJS += ../src/ap/ctrl_iface_ap.o endif @@ -620,14 +709,10 @@ CFLAGS += -DCONFIG_IEEE80211N endif ifdef NEED_AP_MLME -OBJS += ../src/ap/beacon.o OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o -ifdef CONFIG_IEEE80211N -OBJS += ../src/ap/ieee802_11_ht.o -endif CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -707,6 +792,7 @@ endif ifdef NEED_MILENAGE OBJS += ../src/crypto/milenage.o +NEED_AES_ENCBLOCK=y endif ifdef CONFIG_PKCS12 @@ -739,6 +825,10 @@ ifndef CONFIG_TLS CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV11 +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL @@ -758,10 +848,6 @@ ifeq ($(CONFIG_TLS), gnutls) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_gnutls.o LIBS += -lgnutls -lgpg-error -ifdef CONFIG_GNUTLS_EXTRA -CFLAGS += -DCONFIG_GNUTLS_EXTRA -LIBS += -lgnutls-extra -endif endif OBJS += ../src/crypto/crypto_gnutls.o OBJS_p += ../src/crypto/crypto_gnutls.o @@ -959,7 +1045,9 @@ ifdef NEED_FIPS186_2_PRF SHA1OBJS += ../src/crypto/fips_prf_internal.o endif endif -ifndef CONFIG_NO_WPA_PASSPHRASE +ifdef CONFIG_NO_WPA_PASSPHRASE +CFLAGS += -DCONFIG_NO_PBKDF2 +else SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif ifdef NEED_T_PRF @@ -1023,6 +1111,12 @@ OBJS += ../src/crypto/dh_group5.o endif endif +ifdef CONFIG_NO_RANDOM_POOL +CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += ../src/crypto/random.o +endif + ifdef CONFIG_CTRL_IFACE ifeq ($(CONFIG_CTRL_IFACE), y) ifdef CONFIG_NATIVE_WINDOWS @@ -1053,22 +1147,11 @@ DBUS_OBJS += dbus/dbus_old_handlers_wps.o endif DBUS_OBJS += dbus/dbus_dict_helpers.o ifndef DBUS_LIBS -DBUS_LIBS := $(shell pkg-config --libs dbus-1) +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) endif ifndef DBUS_INCLUDE -DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) -endif -dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) -DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) -DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) -ifeq ($(DBUS_VERSION_MAJOR),) -DBUS_VERSION_MAJOR=0 -endif -ifeq ($(DBUS_VERSION_MINOR),) -DBUS_VERSION_MINOR=0 +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif -DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) -DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) DBUS_CFLAGS += $(DBUS_INCLUDE) endif @@ -1081,11 +1164,14 @@ DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o ifdef CONFIG_WPS DBUS_OBJS += dbus/dbus_new_handlers_wps.o endif +ifdef CONFIG_P2P +DBUS_OBJS += dbus/dbus_new_handlers_p2p.o +endif ifndef DBUS_LIBS -DBUS_LIBS := $(shell pkg-config --libs dbus-1) +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) endif ifndef DBUS_INCLUDE -DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO DBUS_OBJS += dbus/dbus_new_introspect.o @@ -1104,8 +1190,14 @@ CFLAGS += $(DBUS_CFLAGS) LIBS += $(DBUS_LIBS) ifdef CONFIG_READLINE -CFLAGS += -DCONFIG_READLINE +OBJS_c += ../src/utils/edit_readline.o LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += ../src/utils/edit.o +else +OBJS_c += ../src/utils/edit_simple.o +endif endif ifdef CONFIG_NATIVE_WINDOWS @@ -1140,12 +1232,6 @@ OBJS += sme.o CFLAGS += -DCONFIG_SME endif -ifdef CONFIG_CLIENT_MLME -OBJS += mlme.o -CFLAGS += -DCONFIG_CLIENT_MLME -NEED_80211_COMMON=y -endif - ifdef NEED_80211_COMMON OBJS += ../src/common/ieee802_11_common.o endif @@ -1160,6 +1246,9 @@ endif ifdef CONFIG_DEBUG_SYSLOG CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif endif ifdef CONFIG_DEBUG_FILE @@ -1184,12 +1273,32 @@ OBJS += bgscan_simple.o NEED_BGSCAN=y endif +ifdef CONFIG_BGSCAN_LEARN +CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.o +NEED_BGSCAN=y +endif + ifdef NEED_BGSCAN CFLAGS += -DCONFIG_BGSCAN OBJS += bgscan.o endif -OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o +ifdef NEED_GAS +OBJS += ../src/common/gas.o +OBJS += gas_query.o +CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.o +CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += ../src/drivers/driver_common.o + +OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o ifdef CONFIG_AUTHENTICATOR OBJS_wpa += tests/link_test.o @@ -1214,6 +1323,9 @@ OBJS_priv += ../src/utils/common.o OBJS_priv += ../src/utils/wpa_debug.o OBJS_priv += ../src/utils/wpabuf.o OBJS_priv += wpa_priv.o +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += ../src/common/ieee802_11_common.o +endif ifdef CONFIG_DRIVER_TEST OBJS_priv += $(SHA1OBJS) OBJS_priv += $(MD5OBJS) @@ -1257,6 +1369,13 @@ ifndef LDO LDO=$(CC) endif +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + dynamic_eap_methods: $(EAPDYN) ../src/drivers/build.wpa_supplicant: @@ -1268,31 +1387,40 @@ dynamic_eap_methods: $(EAPDYN) BCHECK=../src/drivers/build.wpa_supplicant wpa_priv: $(BCHECK) $(OBJS_priv) - $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + @$(E) " LD " $@ wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs) - $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + @$(E) " LD " $@ eapol_test: .config $(OBJS_t) - $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + @$(E) " LD " $@ preauth_test: .config $(OBJS_t2) - $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + @$(E) " LD " $@ wpa_passphrase: $(OBJS_p) - $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + @$(E) " LD " $@ wpa_cli: $(OBJS_c) - $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + @$(E) " LD " $@ link_test: $(OBJS) $(OBJS_h) tests/link_test.o - $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + @$(E) " LD " $@ test_wpa: $(OBJS_wpa) $(OBJS_h) - $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + @$(E) " LD " $@ win_if_list: win_if_list.c - $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + @$(E) " LD " $@ eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ @@ -1318,17 +1446,13 @@ eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_com $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init -Q=@ -E=echo -ifeq ($(V), 1) -Q= -E=true -endif - %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< +%.service: %.service.in + sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + wpa_supplicant.exe: wpa_supplicant mv -f $< $@ wpa_cli.exe: wpa_cli diff --git a/wpa_supplicant/README b/wpa_supplicant/README index 45c8bae..f324192 100644 --- a/wpa_supplicant/README +++ b/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2010, Jouni Malinen and contributors +Copyright (c) 2003-2012, Jouni Malinen and contributors All Rights Reserved. This program is dual-licensed under both the GPL version 2 and BSD @@ -147,22 +147,12 @@ Current hardware/software requirements: Linuxant DriverLoader (http://www.linuxant.com/driverloader/) with Windows NDIS driver for your wlan card supporting WPA. - Agere Systems Inc. Linux Driver - (http://www.agere.com/support/drivers/) - Please note that the driver interface file (driver_hermes.c) and - hardware specific include files are not included in the - wpa_supplicant distribution. You will need to copy these from the - source package of the Agere driver. - madwifi driver for cards based on Atheros chip set (ar521x) (http://sourceforge.net/projects/madwifi/) Please note that you will need to modify the wpa_supplicant .config file to use the correct path for the madwifi driver root directory (CFLAGS += -I../madwifi/wpa line in example defconfig). - ATMEL AT76C5XXx driver for USB and PCMCIA cards - (http://atmelwlandriver.sourceforge.net/). - Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with Windows NDIS driver. @@ -181,12 +171,6 @@ Current hardware/software requirements: not need (or even work) with the specific driver wrapper. Use -Dwext with that driver. - Intel ipw2100 driver - (http://sourceforge.net/projects/ipw2100/) - - Intel ipw2200 driver - (http://sourceforge.net/projects/ipw2200/) - In theory, any driver that supports Linux wireless extensions can be used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in configuration file. @@ -396,18 +380,13 @@ authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite CONFIG_PCSC=y Following options can be added to .config to select which driver -interfaces are included. Hermes driver interface needs to be downloaded -from Agere (see above). +interfaces are included. CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_HERMES=y CONFIG_DRIVER_MADWIFI=y -CONFIG_DRIVER_ATMEL=y CONFIG_DRIVER_WEXT=y CONFIG_DRIVER_RALINK=y -CONFIG_DRIVER_NDISWRAPPER=y CONFIG_DRIVER_BROADCOM=y -CONFIG_DRIVER_IPW=y CONFIG_DRIVER_BSD=y CONFIG_DRIVER_NDIS=y @@ -415,13 +394,9 @@ Following example includes all features and driver interfaces that are included in the wpa_supplicant package: CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_HERMES=y CONFIG_DRIVER_MADWIFI=y -CONFIG_DRIVER_ATMEL=y CONFIG_DRIVER_WEXT=y -CONFIG_DRIVER_NDISWRAPPER=y CONFIG_DRIVER_BROADCOM=y -CONFIG_DRIVER_IPW=y CONFIG_DRIVER_BSD=y CONFIG_DRIVER_NDIS=y CONFIG_IEEE8021X_EAPOL=y @@ -516,14 +491,10 @@ options: drivers: hostap = Host AP driver (Intersil Prism2/2.5/3) [default] (this can also be used with Linuxant DriverLoader) - hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext) - atmel = ATMEL AT76C5XXx (USB, PCMCIA) wext = Linux wireless extensions (generic) ralink = Ralink Client driver - ndiswrapper = Linux ndiswrapper (deprecated; use wext) broadcom = Broadcom wl.o driver - ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer) wired = wpa_supplicant wired Ethernet driver roboswitch = wpa_supplicant Broadcom switch driver bsd = BSD 802.11 support (Atheros, etc.) @@ -894,13 +865,14 @@ script: IFNAME=$1 CMD=$2 -if [ "$CMD" == "CONNECTED" ]; then +if [ "$CMD" = "CONNECTED" ]; then SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=` # configure network, signal DHCP client, etc. fi -if [ "$CMD" == "DISCONNECTED" ]; then +if [ "$CMD" = "DISCONNECTED" ]; then # remove network configuration, if needed + SSID= fi diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P new file mode 100644 index 0000000..db6e4ae --- /dev/null +++ b/wpa_supplicant/README-P2P @@ -0,0 +1,525 @@ +wpa_supplicant and Wi-Fi P2P +============================ + +This document describes how the Wi-Fi P2P implementation in +wpa_supplicant can be configured and how an external component on the +client (e.g., management GUI) is used to enable WPS enrollment and +registrar registration. + + +Introduction to Wi-Fi P2P +------------------------- + +TODO + +More information about Wi-Fi P2P is available from Wi-Fi Alliance: +http://www.wi-fi.org/Wi-Fi_Direct.php + + +wpa_supplicant implementation +----------------------------- + +TODO + + +wpa_supplicant configuration +---------------------------- + +Wi-Fi P2P is an optional component that needs to be enabled in the +wpa_supplicant build configuration (.config). Here is an example +configuration that includes Wi-Fi P2P support and Linux nl80211 +-based driver interface: + +CONFIG_DRIVER_NL80211=y +CONFIG_CTRL_IFACE=y +CONFIG_P2P=y +CONFIG_AP=y +CONFIG_WPS=y + + +In run-time configuration file (wpa_supplicant.conf), some parameters +for P2P may be set. In order to make the devices easier to recognize, +device_name and device_type should be specified. For example, +something like this should be included: + +ctrl_interface=/var/run/wpa_supplicant +device_name=My P2P Device +device_type=1-0050F204-1 + + +wpa_cli +------- + +Actual Wi-Fi P2P operations are requested during runtime. These can be +done for example using wpa_cli (which is described below) or a GUI +like wpa_gui-qt4. + + +wpa_cli starts in interactive mode if no command string is included on +the command line. By default, it will select the first network interface +that it can find (and that wpa_supplicant controls). If more than one +interface is in use, it may be necessary to select one of the explicitly +by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1'). + +Most of the P2P operations are done on the main interface (e.g., the +interface that is automatically added when the driver is loaded, e.g., +wlan0). When using a separate virtual interface for group operations +(e.g., wlan1), the control interface for that group interface may need +to be used for some operations (mainly WPS activation in GO). This may +change in the future so that all the needed operations could be done +over the main control interface. + +Device Discovery + +p2p_find [timeout in seconds] [type=] + +The default behavior is to run a single full scan in the beginning and +then scan only social channels. type=social will scan only social +channels, i.e., it skips the initial full scan. type=progressive is +like the default behavior, but it will scan through all the channels +progressively one channel at the time in the Search state rounds. This +will help in finding new groups or groups missed during the initial +full scan. + +p2p_listen [timeout in seconds] + +Start Listen-only state (become discoverable without searching for +other devices). Optional parameter can be used to specify the duration +for the Listen operation in seconds. This command may not be of that +much use during normal operations and is mainly designed for +testing. It can also be used to keep the device discoverable without +having to maintain a group. + +p2p_stop_find + +Stop ongoing P2P device discovery or other operation (connect, listen +mode). + +p2p_flush + +Flush P2P peer table and state. + +Group Formation + +p2p_prov_disc [join] + +Send P2P provision discovery request to the specified peer. The +parameters for this command are the P2P device address of the peer and +the desired configuration method. For example, "p2p_prov_disc +02:01:02:03:04:05 display" would request the peer to display a PIN for +us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer +to enter a PIN that we display. + +The optional "join" parameter can be used to indicate that this command +is requesting an already running GO to prepare for a new client. This is +mainly used with "display" to request it to display a PIN. + +p2p_connect [display|keypad] + [persistent] [join|auth] [go_intent=<0..15>] [freq=] + +Start P2P group formation with a discovered P2P peer. This includes +optional group owner negotiation, group interface setup, provisioning, +and establishing data connection. + +The parameter specifies the WPS provisioning +method. "pbc" string starts pushbutton method, "pin" string start PIN +method using an automatically generated PIN (which will be returned as +the command return code), PIN# means that a pre-selected PIN can be +used (e.g., 12345670). [display|keypad] is used with PIN method +to specify which PIN is used (display=dynamically generated random PIN +from local display, keypad=PIN entered from peer display). "persistent" +parameter can be used to request a persistent group to be formed. + +"join" indicates that this is a command to join an existing group as a +client. It skips the GO Negotiation part. This will send a Provision +Discovery Request message to the target GO before associating for WPS +provisioning. + +"auth" indicates that the WPS parameters are authorized for the peer +device without actually starting GO Negotiation (i.e., the peer is +expected to initiate GO Negotiation). This is mainly for testing +purposes. + +"go_intent" can be used to override the default GO Intent for this GO +Negotiation. + +"freq" can be used to set a forced operating channel (e.g., freq=2412 +to select 2.4 GHz channel 1). + +p2p_group_add [persistent|persistent=] [freq=] + +Set up a P2P group owner manually (i.e., without group owner +negotiation with a specific peer). This is also known as autonomous +GO. Optional persistent= can be used to specify restart of +a persistent group. Optional freq= can be used to force +the GO to be started on a specific frequency. Special freq=2 or freq=5 +options can be used to request the best 2.4 GHz or 5 GHz band channel +to be selected automatically. + +p2p_reject + +Reject connection attempt from a peer (specified with a device +address). This is a mechanism to reject a pending GO Negotiation with +a peer and request to automatically block any further connection or +discovery of the peer. + +p2p_group_remove + +Terminate a P2P group. If a new virtual network interface was used for +the group, it will also be removed. The network interface name of the +group interface is used as a parameter for this command. + +p2p_cancel + +Cancel an ongoing P2P group formation related operation. + +Service Discovery + +p2p_serv_disc_req + +Schedule a P2P service discovery request. The parameters for this +command are the device address of the peer device (or 00:00:00:00:00:00 +for wildcard query that is sent to every discovered P2P peer that +supports service discovery) and P2P Service Query TLV(s) as hexdump. For +example, + +p2p_serv_disc_req 00:00:00:00:00:00 02000001 + +schedules a request for listing all available services of all service +discovery protocols and requests this to be sent to all discovered +peers (note: this can result in long response frames). The pending +requests are sent during device discovery (see p2p_find). + +Only a single pending wildcard query is supported, but there can be +multiple pending peer device specific queries (each will be sent in +sequence whenever the peer is found). + +This command returns an identifier for the pending query (e.g., +"1f77628") that can be used to cancel the request. Directed requests +will be automatically removed when the specified peer has replied to +it. + +For UPnP, an alternative command format can be used to specify a +single query TLV (i.e., a service discovery for a specific UPnP +service): + +p2p_serv_disc_req 00:00:00:00:00:00 upnp + +For example: + +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +Additional examples for queries: + +# list of all Bonjour services +p2p_serv_disc_req 00:00:00:00:00:00 02000101 + +# list of all UPnP services +p2p_serv_disc_req 00:00:00:00:00:00 02000201 + +# list of all WS-Discovery services +p2p_serv_disc_req 00:00:00:00:00:00 02000301 + +# list of all Bonjour and UPnP services +p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202 + +# Apple File Sharing over TCP +p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01 + +# Bonjour SSTH (supported service type hash) +p2p_serv_disc_req 00:00:00:00:00:00 05000101000000 + +# UPnP examples +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 +p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +p2p_serv_disc_cancel_req + +Cancel a pending P2P service discovery request. This command takes a +single parameter: identifier for the pending query (the value returned +by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628". + +p2p_serv_disc_resp + +Reply to a service discovery query. This command takes following +parameters: frequency in MHz, destination address, dialog token, +response TLV(s). The first three parameters are copied from the +request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7 +1 0300000101". This command is used only if external program is used +to process the request (see p2p_serv_disc_external). + +p2p_service_update + +Indicate that local services have changed. This is used to increment +the P2P service indicator value so that peers know when previously +cached information may have changed. This is only needed when external +service discovery processing is enabled since the commands to +pre-configure services for internal processing will increment the +indicator automatically. + +p2p_serv_disc_external <0|1> + +Configure external processing of P2P service requests: 0 (default) = +no external processing of requests (i.e., internal code will process +each request based on pre-configured services), 1 = external +processing of requests (external program is responsible for replying +to service discovery requests with p2p_serv_disc_resp). Please note +that there is quite strict limit on how quickly the response needs to +be transmitted, so use of the internal processing is strongly +recommended. + +p2p_service_add bonjour + +Add a local Bonjour service for internal SD query processing. + +Examples: + +# AFP Over TCP (PTR) +p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027 +# AFP Over TCP (TXT) (RDATA=null) +p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00 + +# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.) +p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027 +# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript) +p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074 + +# Supported Service Type Hash (SSTH) +p2p_service_add bonjour 000000 <32-byte bitfield as hexdump> +(note: see P2P spec Annex E.4 for information on how to construct the bitfield) + +p2p_service_del bonjour + +Remove a local Bonjour service from internal SD query processing. + +p2p_service_add upnp + +Add a local UPnP service for internal SD query processing. + +Examples: + +p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice +p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice +p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2 +p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +p2p_service_del upnp + +Remove a local UPnP service from internal SD query processing. + +p2p_service_flush + +Remove all local services from internal SD query processing. + +Invitation + +p2p_invite [persistent=|group=] [peer=address] + [go_dev_addr=address] + +Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a +persistent group (e.g., persistent=4). If the peer device is the GO of +the persisten group, the peer parameter is not needed. Otherwise it is +used to specify which device to invite. go_dev_addr parameter can be +used to override the GO device address for Invitation Request should +it be not known for some reason (this should not be needed in most +cases). + +Group Operations + +(These are used on the group interface.) + +wps_pin + +Start WPS PIN method. This allows a single WPS Enrollee to connect to +the AP/GO. This is used on the GO when a P2P client joins an existing +group. The second parameter is the address of the Enrollee or a string +"any" to allow any station to use the entered PIN (which will restrict +the PIN for one-time-use). PIN is the Enrollee PIN read either from a +label or display on the P2P Client/WPS Enrollee. + +wps_pbc + +Start WPS PBC method (i.e., push the button). This allows a single WPS +Enrollee to connect to the AP/GO. This is used on the GO when a P2P +client joins an existing group. + +p2p_get_passphrase + +Get the passphrase for a group (only available when acting as a GO). + +p2p_presence_req [ ] [ ] + +Send a P2P Presence Request to the GO (this is only available when +acting as a P2P client). If no duration/interval pairs are given, the +request indicates that this client has no special needs for GO +presence. the first parameter pair gives the preferred duration and +interval values in microseconds. If the second pair is included, that +indicates which value would be acceptable. + +Parameters + +p2p_ext_listen [ ] + +Configure Extended Listen Timing. If the parameters are omitted, this +feature is disabled. If the parameters are included, Listen State will +be entered every interval msec for at least period msec. Both values +have acceptable range of 1-65535 (with interval obviously having to be +larger than or equal to duration). If the P2P module is not idle at +the time the Extended Listen Timing timeout occurs, the Listen State +operation will be skipped. + +The configured values will also be advertised to other P2P Devices. The +received values are available in the p2p_peer command output: + +ext_listen_period=100 ext_listen_interval=5000 + +p2p_set + +Change dynamic P2P parameters + +p2p_set discoverability <0/1> + +Disable/enable advertisement of client discoverability. This is +enabled by default and this parameter is mainly used to allow testing +of device discoverability. + +p2p_set managed <0/1> + +Disable/enable managed P2P Device operations. This is disabled by +default. + +p2p_set listen_channel <1/6/11> + +Set P2P Listen channel. This is mainly meant for testing purposes and +changing the Listen channel during normal operations can result in +protocol failures. + +p2p_set ssid_postfix + +Set postfix string to be added to the automatically generated P2P SSID +(DIRECT-). For example, postfix of "-testing" +could result in the SSID becoming DIRECT-ab-testing. + +set + +Set global configuration parameters which may also affect P2P +operations. The format on these parameters is same as is used in +wpa_supplicant.conf. Only the parameters listen here should be +changed. Modifying other parameters may result in incorrect behavior +since not all existing users of the parameters are updated. + +set uuid + +Set WPS UUID (by default, this is generated based on the MAC address). + +set device_name + +Set WPS Device Name (also included in some P2P messages). + +set manufacturer + +Set WPS Manufacturer. + +set model_name + +Set WPS Model Name. + +set model_number + +Set WPS Model Number. + +set serial_number + +Set WPS Serial Number. + +set device_type + +Set WPS Device Type. + +set os_version + +Set WPS OS Version. + +set config_methods + +Set WPS Configuration Methods. + +set sec_device_type + +Add a new Secondary Device Type. + +set p2p_go_intent + +Set the default P2P GO Intent. Note: This value can be overridden in +p2p_connect command and as such, there should be no need to change the +default value here during normal operations. + +set p2p_ssid_postfix + +Set P2P SSID postfix. + +set persistent_reconnect <0/1> + +Disable/enabled persistent reconnect for reinvocation of persistent +groups. If enabled, invitations to reinvoke a persistent group will be +accepted without separate authorization (e.g., user interaction). + +set country + +Set country code (this is included in some P2P messages). + +Status + +p2p_peers [discovered] + +List P2P Device Addresses of all the P2P peers we know. The optional +"discovered" parameter filters out the peers that we have not fully +discovered, i.e., which we have only seen in a received Probe Request +frame. + +p2p_peer + +Fetch information about a known P2P peer. + +Group Status + +(These are used on the group interface.) + +status + +Show status information (connection state, role, use encryption +parameters, IP address, etc.). + +sta + +Show information about an associated station (when acting in AP/GO role). + +all_sta + +Lists the currently associated stations. + +Configuration data + +list_networks + +Lists the configured networks, including stored information for +persistent groups. The identifier in this list is used with +p2p_group_add and p2p_invite to indicate which persistent group is to +be reinvoked. + +remove_network + +Remove a network entry from configuration. + + +wpa_cli action script +--------------------- + +See examples/p2p-action.sh + +TODO: describe DHCP/DNS setup +TODO: cross-connection diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS index 8f0d0d6..bf75cb4 100644 --- a/wpa_supplicant/README-WPS +++ b/wpa_supplicant/README-WPS @@ -47,9 +47,7 @@ wpa_supplicant implementation wpa_supplicant includes an optional WPS component that can be used as an Enrollee to enroll new network credential or as a Registrar to -configure an AP. The current version of wpa_supplicant does not -support operation as an external WLAN Management Registrar for adding -new client devices or configuring the AP over UPnP. +configure an AP. wpa_supplicant configuration @@ -57,11 +55,17 @@ wpa_supplicant configuration WPS is an optional component that needs to be enabled in wpa_supplicant build configuration (.config). Here is an example -configuration that includes WPS support and Linux wireless extensions --based driver interface: +configuration that includes WPS support and Linux nl80211 -based +driver interface: -CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NL80211=y CONFIG_WPS=y +CONFIG_WPS2=y + +If you want to enable WPS external registrar (ER) functionality, you +will also need to add following line: + +CONFIG_WPS_ER=y WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for @@ -93,6 +97,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the network. wpa_supplicant uses the control interface as an input channel for these events. +The PIN value used in the commands must be processed by an UI to +remove non-digit characters and potentially, to verify the checksum +digit. "wpa_cli wps_check_pin " can be used to do such processing. +It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum +digit is incorrect, or the processed PIN (non-digit characters removed) +if the PIN is valid. + If the client device has a display, a random PIN has to be generated for each WPS registration session. wpa_supplicant can do this with a control interface request, e.g., by calling wpa_cli: @@ -116,6 +127,11 @@ This starts the WPS negotiation in the same way as above with the generated PIN. +If a random PIN is needed for a user interface, "wpa_cli wps_pin get" +can be used to generate a new PIN without starting WPS negotiation. +This random PIN can then be passed as an argument to another wps_pin +call when the actual operation should be started. + If the client design wants to support optional WPS PBC mode, this can be enabled by either a physical button in the client device or a virtual button in the user interface. The PBC operation requires that @@ -198,3 +214,92 @@ Following control interface messages are sent out for external programs: WPS-CRED-RECEIVED For example: <2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727 + + +wpa_supplicant as WPS External Registrar (ER) +--------------------------------------------- + +wpa_supplicant can be used as a WPS ER to configure an AP or enroll +new Enrollee to join the network. This functionality uses UPnP and +requires that a working IP connectivity is available with the AP (this +can be either over a wired or wireless connection). + +Separate wpa_supplicant process can be started for WPS ER +operations. A special "none" driver can be used in such a case to +indicate that no local network interface is actually controlled. For +example, following command could be used to start the ER: + +wpa_supplicant -Dnone -c er.conf -ieth0 + +Sample er.conf: + +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin +device_name=WPS External Registrar + + +wpa_cli commands for ER functionality: + +wps_er_start [IP address] +- start WPS ER functionality +- the optional IP address parameter can be used to filter operations only + to include a single AP +- if run again while ER is active, the stored information (discovered APs + and Enrollees) are shown again + +wps_er_stop +- stop WPS ER functionality + +wps_er_learn +- learn AP configuration + +wps_er_set_config +- use AP configuration from a locally configured network (e.g., from + wps_reg command); this does not change the AP's configuration, but + only prepares a configuration to be used when enrolling a new device + to the AP + +wps_er_config +- examples: + wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678 + wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE "" + + must be one of the following: OPEN WPAPSK WPA2PSK + must be one of the following: NONE WEP TKIP CCMP + + +wps_er_pbc +- accept an Enrollee PBC using External Registrar + +wps_er_pin [Enrollee MAC address] +- add an Enrollee PIN to External Registrar +- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN +- if the MAC address of the enrollee is known, it should be configured + to allow the AP to advertise list of authorized enrollees + + +WPS ER events: + +WPS_EVENT_ER_AP_ADD +- WPS ER discovered an AP + +WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/ + +WPS_EVENT_ER_AP_REMOVE +- WPS ER removed an AP entry + +WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 + +WPS_EVENT_ER_ENROLLEE_ADD +- WPS ER discovered a new Enrollee + +WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345| + +WPS_EVENT_ER_ENROLLEE_REMOVE +- WPS ER removed an Enrollee entry + +WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 + +WPS-ER-AP-SETTINGS +- WPS ER learned AP settings + +WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678 diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 2b93984..75b1393 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -16,12 +16,17 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "ap/hostapd.h" #include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" #ifdef NEED_AP_MLME #include "ap/ieee802_11.h" #endif /* NEED_AP_MLME */ +#include "ap/beacon.h" #include "ap/ieee802_1x.h" #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" @@ -29,11 +34,20 @@ #include "eap_server/eap_methods.h" #include "eap_common/eap_wsc_common.h" #include "wps/wps.h" +#include "common/ieee802_11_defs.h" #include "config_ssid.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" +#include "p2p_supplicant.h" #include "ap.h" +#include "ap/sta_info.h" +#include "notify.h" + + +#ifdef CONFIG_WPS +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); +#endif /* CONFIG_WPS */ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, @@ -64,9 +78,59 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, return -1; } - /* TODO: enable HT if driver supports it; + /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ +#ifdef CONFIG_IEEE80211N + /* + * Enable HT20 if the driver supports it, by setting conf->ieee80211n. + * Using default config settings for: conf->ht_op_mode_fixed, + * conf->ht_capab, conf->secondary_channel, conf->require_ht + */ + if (wpa_s->hw.modes) { + struct hostapd_hw_modes *mode = NULL; + int i; + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == conf->hw_mode) { + mode = &wpa_s->hw.modes[i]; + break; + } + } + if (mode && mode->ht_capab) + conf->ieee80211n = 1; + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_P2P + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + /* Remove 802.11b rates from supported and basic rate sets */ + int *list = os_malloc(4 * sizeof(int)); + if (list) { + list[0] = 60; + list[1] = 120; + list[2] = 240; + list[3] = -1; + } + conf->basic_rates = list; + + list = os_malloc(9 * sizeof(int)); + if (list) { + list[0] = 60; + list[1] = 90; + list[2] = 120; + list[3] = 180; + list[4] = 240; + list[5] = 360; + list[6] = 480; + list[7] = 540; + list[8] = -1; + } + conf->supported_rates = list; + } + + bss->isolate = !wpa_s->conf->p2p_intra_bss; +#endif /* CONFIG_P2P */ + if (ssid->ssid_len == 0) { wpa_printf(MSG_ERROR, "No SSID configured for AP mode"); return -1; @@ -76,6 +140,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->ssid.ssid_len = ssid->ssid_len; bss->ssid.ssid_set = 1; + if (ssid->auth_alg) + bss->auth_algs = ssid->auth_alg; + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) bss->wpa = ssid->proto; bss->wpa_key_mgmt = ssid->key_mgmt; @@ -89,6 +156,22 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; + } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] || + ssid->wep_key_len[2] || ssid->wep_key_len[3]) { + struct hostapd_wep_keys *wep = &bss->ssid.wep; + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] == 0) + continue; + wep->key[i] = os_malloc(ssid->wep_key_len[i]); + if (wep->key[i] == NULL) + return -1; + os_memcpy(wep->key[i], ssid->wep_key[i], + ssid->wep_key_len[i]); + wep->len[i] = ssid->wep_key_len[i]; + } + wep->idx = ssid->wep_tx_keyidx; + wep->keys_set = 1; } /* Select group cipher based on the enabled pairwise cipher suites */ @@ -110,46 +193,187 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, else if (bss->wpa) bss->ssid.security_policy = SECURITY_WPA_PSK; else if (bss->ieee802_1x) { + int cipher = WPA_CIPHER_NONE; bss->ssid.security_policy = SECURITY_IEEE_802_1X; bss->ssid.wep.default_len = bss->default_wep_key_len; - } else if (bss->ssid.wep.keys_set) + if (bss->default_wep_key_len) + cipher = bss->default_wep_key_len >= 13 ? + WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else if (bss->ssid.wep.keys_set) { + int cipher = WPA_CIPHER_WEP40; + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; bss->ssid.security_policy = SECURITY_STATIC_WEP; - else + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; + } #ifdef CONFIG_WPS /* - * Enable WPS by default, but require user interaction to actually use - * it. Only the internal Registrar is supported. + * Enable WPS by default for open and WPA/WPA2-Personal network, but + * require user interaction to actually use it. Only the internal + * Registrar is supported. */ + if (bss->ssid.security_policy != SECURITY_WPA_PSK && + bss->ssid.security_policy != SECURITY_PLAINTEXT) + goto no_wps; +#ifdef CONFIG_WPS2 + if (bss->ssid.security_policy == SECURITY_WPA_PSK && + (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2))) + goto no_wps; /* WPS2 does not allow WPA/TKIP-only + * configuration */ +#endif /* CONFIG_WPS2 */ bss->eap_server = 1; bss->wps_state = 2; - bss->ap_setup_locked = 1; + bss->ap_setup_locked = 2; if (wpa_s->conf->config_methods) bss->config_methods = os_strdup(wpa_s->conf->config_methods); - if (wpa_s->conf->device_type) - bss->device_type = os_strdup(wpa_s->conf->device_type); + os_memcpy(bss->device_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + if (wpa_s->conf->device_name) { + bss->device_name = os_strdup(wpa_s->conf->device_name); + bss->friendly_name = os_strdup(wpa_s->conf->device_name); + } + if (wpa_s->conf->manufacturer) + bss->manufacturer = os_strdup(wpa_s->conf->manufacturer); + if (wpa_s->conf->model_name) + bss->model_name = os_strdup(wpa_s->conf->model_name); + if (wpa_s->conf->model_number) + bss->model_number = os_strdup(wpa_s->conf->model_number); + if (wpa_s->conf->serial_number) + bss->serial_number = os_strdup(wpa_s->conf->serial_number); + if (is_nil_uuid(wpa_s->conf->uuid)) + os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN); + else + os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); + os_memcpy(bss->os_version, wpa_s->conf->os_version, 4); +no_wps: #endif /* CONFIG_WPS */ + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + bss->max_num_sta = wpa_s->max_stations; + else + bss->max_num_sta = wpa_s->conf->max_num_sta; + + bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; + return 0; } static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq) { +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return; + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + len - hdr_len, freq); +#endif /* CONFIG_P2P */ +} + + +static void ap_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + + if (event == WPS_EV_FAIL) { + struct wps_event_fail *fail = &data->fail; + + if (wpa_s->parent && wpa_s->parent != wpa_s && + wpa_s == wpa_s->global->p2p_group_formation) { + /* + * src/ap/wps_hostapd.c has already sent this on the + * main interface, so only send on the parent interface + * here if needed. + */ + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d", + fail->msg, fail->config_error); + } + wpas_p2p_wps_failed(wpa_s, fail); + } +#endif /* CONFIG_P2P */ +} + + +static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr, + int authorized, const u8 *p2p_dev_addr) +{ + wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr); +} + + +static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return -1; + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + len - hdr_len, freq); +#endif /* CONFIG_P2P */ + return 0; } -static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie, - size_t ie_len) +static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len) { +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len); +#else /* CONFIG_P2P */ return 0; +#endif /* CONFIG_P2P */ } static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e) { +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + wpas_p2p_wps_success(wpa_s, mac_addr, 1); +#endif /* CONFIG_P2P */ +} + + +static void wpas_ap_configured_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + + if (wpa_s->ap_configured_cb) + wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx, + wpa_s->ap_configured_cb_data); } @@ -182,11 +406,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, params.mode = IEEE80211_MODE_IBSS; break; case WPAS_MODE_AP: + case WPAS_MODE_P2P_GO: + case WPAS_MODE_P2P_GROUP_FORMATION: params.mode = IEEE80211_MODE_AP; break; } params.freq = ssid->frequency; + params.wpa_proto = ssid->proto; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; else @@ -207,6 +434,17 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); params.group_suite = params.pairwise_suite; +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + params.p2p = 1; +#endif /* CONFIG_P2P */ + + if (wpa_s->parent->set_ap_uapsd) + params.uapsd = wpa_s->parent->ap_uapsd; + else + params.uapsd = -1; + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; @@ -216,6 +454,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (hapd_iface == NULL) return -1; hapd_iface->owner = wpa_s; + hapd_iface->drv_flags = wpa_s->drv_flags; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { @@ -223,12 +462,25 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } + if (params.uapsd > 0) { + conf->bss->wmm_enabled = 1; + conf->bss->wmm_uapsd = 1; + } + if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) { wpa_printf(MSG_ERROR, "Failed to create AP configuration"); wpa_supplicant_ap_deinit(wpa_s); return -1; } +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO) + conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER | + P2P_GROUP_FORMATION; +#endif /* CONFIG_P2P */ + hapd_iface->num_bss = conf->num_bss; hapd_iface->bss = os_zalloc(conf->num_bss * sizeof(struct hostapd_data *)); @@ -247,42 +499,64 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, } hapd_iface->bss[i]->msg_ctx = wpa_s; + hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent; hapd_iface->bss[i]->public_action_cb = ap_public_action_rx; hapd_iface->bss[i]->public_action_cb_ctx = wpa_s; + hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx; + hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s; hostapd_register_probereq_cb(hapd_iface->bss[i], ap_probe_req_rx, wpa_s); hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb; hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s; + hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb; + hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s; + hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb; + hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s; +#ifdef CONFIG_P2P + hapd_iface->bss[i]->p2p = wpa_s->global->p2p; + hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init( + wpa_s, ssid->p2p_persistent_group, + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION); +#endif /* CONFIG_P2P */ + hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; + hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; } os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN); hapd_iface->bss[0]->driver = wpa_s->driver; hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv; + wpa_s->current_ssid = ssid; + os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); + wpa_s->assoc_freq = ssid->frequency; + if (hostapd_setup_interface(wpa_s->ap_iface)) { wpa_printf(MSG_ERROR, "Failed to initialize AP interface"); wpa_supplicant_ap_deinit(wpa_s); return -1; } - wpa_s->current_ssid = ssid; - os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); - wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); - - if (wpa_s->ap_configured_cb) - wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx, - wpa_s->ap_configured_cb_data); - return 0; } void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) { +#ifdef CONFIG_WPS + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +#endif /* CONFIG_WPS */ + if (wpa_s->ap_iface == NULL) return; wpa_s->current_ssid = NULL; + wpa_s->assoc_freq = 0; + wpa_s->reassociated_connection = 0; +#ifdef CONFIG_P2P + if (wpa_s->ap_iface->bss) + wpa_s->ap_iface->bss[0]->p2p_group = NULL; + wpas_p2p_group_deinit(wpa_s); +#endif /* CONFIG_P2P */ hostapd_interface_deinit(wpa_s->ap_iface); hostapd_interface_free(wpa_s->ap_iface); wpa_s->ap_iface = NULL; @@ -300,16 +574,21 @@ void ap_tx_status(void *ctx, const u8 *addr, } -void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len) +void ap_client_poll_ok(void *ctx, const u8 *addr) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; - const struct ieee80211_hdr *hdr = - (const struct ieee80211_hdr *) frame; - u16 fc = le_to_host16(hdr->frame_control); - ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2, - (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == - (WLAN_FC_TODS | WLAN_FC_FROMDS)); + if (wpa_s->ap_iface) + hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr); +#endif /* NEED_AP_MLME */ +} + + +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds); #endif /* NEED_AP_MLME */ } @@ -346,11 +625,58 @@ void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WPS -int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *p2p_dev_addr) { if (!wpa_s->ap_iface) return -1; - return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0]); + return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0], + p2p_dev_addr); +} + + +static int wpa_supplicant_ap_wps_sta_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta && (sta->flags & WLAN_STA_WPS)) { + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, + __func__, MAC2STR(sta->addr)); + return 1; + } + + return 0; +} + + +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s) +{ + struct wps_registrar *reg; + int reg_sel = 0, wps_sta = 0; + + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps) + return -1; + + reg = wpa_s->ap_iface->bss[0]->wps->registrar; + reg_sel = wps_registrar_wps_cancel(reg); + wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0], + wpa_supplicant_ap_wps_sta_cancel, NULL); + + if (!reg_sel && !wps_sta) { + wpa_printf(MSG_DEBUG, "No WPS operation in progress at this " + "time"); + return -1; + } + + /* + * There are 2 cases to return wps cancel as success: + * 1. When wps cancel was initiated but no connection has been + * established with client yet. + * 2. Client is in the middle of exchanging WPS messages. + */ + + return 0; } @@ -364,16 +690,135 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, if (pin == NULL) { unsigned int rpin = wps_generate_pin(); - ret_len = os_snprintf(buf, buflen, "%d", rpin); + ret_len = os_snprintf(buf, buflen, "%08d", rpin); pin = buf; - } + } else + ret_len = os_snprintf(buf, buflen, "%s", pin); - ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0); + ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, + 0); if (ret) return -1; return ret_len; } + +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + wpas_wps_ap_pin_disable(wpa_s); +} + + +static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hapd = wpa_s->ap_iface->bss[0]; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + unsigned int pin; + char pin_txt[9]; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + pin = wps_generate_pin(); + os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return NULL; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return hapd->conf->ap_pin; +} + + +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hapd->conf->ap_pin; +} + + +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout) +{ + struct hostapd_data *hapd; + char pin_txt[9]; + int ret; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(pin_txt)) + return -1; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return -1; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return 0; +} + + +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Disable AP + * PIN if this happens multiple times to slow down brute force attacks. + */ + hapd->ap_pin_failures++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", + hapd->ap_pin_failures); + if (hapd->ap_pin_failures < 3) + return; + + wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN"); + hapd->ap_pin_failures = 0; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; +} + #endif /* CONFIG_WPS */ @@ -440,6 +885,35 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, #endif /* CONFIG_CTRL_IFACE */ +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) +{ + struct hostapd_iface *iface = wpa_s->ap_iface; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_data *hapd; + + if (ssid == NULL || wpa_s->ap_iface == NULL || + ssid->mode == WPAS_MODE_INFRA || + ssid->mode == WPAS_MODE_IBSS) + return -1; + +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GO) + iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER | + P2P_GROUP_FORMATION; +#endif /* CONFIG_P2P */ + + hapd = iface->bss[0]; + if (hapd->drv_priv == NULL) + return -1; + ieee802_11_set_beacons(iface); + hostapd_set_ap_wps_ie(hapd); + + return 0; +} + + int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr) { diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 381a432..567e784 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -21,9 +21,16 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s); void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s, const u8 *src_addr, const u8 *buf, size_t len); -int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *p2p_dev_addr); int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, char *buf, size_t buflen); +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s); +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s); +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout); +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s); +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout); int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, @@ -34,10 +41,13 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose); void ap_tx_status(void *ctx, const u8 *addr, const u8 *buf, size_t len, int ack); -void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len); +void ap_client_poll_ok(void *ctx, const u8 *addr); +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds); void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt); void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok); +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s); int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr); +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); #endif /* AP_H */ diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c index 31b5d27..5661830 100644 --- a/wpa_supplicant/bgscan.c +++ b/wpa_supplicant/bgscan.c @@ -22,11 +22,17 @@ #ifdef CONFIG_BGSCAN_SIMPLE extern const struct bgscan_ops bgscan_simple_ops; #endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN +extern const struct bgscan_ops bgscan_learn_ops; +#endif /* CONFIG_BGSCAN_LEARN */ static const struct bgscan_ops * bgscan_modules[] = { #ifdef CONFIG_BGSCAN_SIMPLE &bgscan_simple_ops, #endif /* CONFIG_BGSCAN_SIMPLE */ +#ifdef CONFIG_BGSCAN_LEARN + &bgscan_learn_ops, +#endif /* CONFIG_BGSCAN_LEARN */ NULL }; @@ -88,10 +94,12 @@ void bgscan_deinit(struct wpa_supplicant *wpa_s) } -int bgscan_notify_scan(struct wpa_supplicant *wpa_s) +int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) { if (wpa_s->bgscan && wpa_s->bgscan_priv) - return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv); + return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv, + scan_res); return 0; } @@ -103,8 +111,13 @@ void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) } -void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above) +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, + int current_signal, int current_noise, + int current_txrate) { if (wpa_s->bgscan && wpa_s->bgscan_priv) - wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above); + wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above, + current_signal, + current_noise, + current_txrate); } diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h index 69e99b6..ae94a48 100644 --- a/wpa_supplicant/bgscan.h +++ b/wpa_supplicant/bgscan.h @@ -25,18 +25,24 @@ struct bgscan_ops { const struct wpa_ssid *ssid); void (*deinit)(void *priv); - int (*notify_scan)(void *priv); + int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res); void (*notify_beacon_loss)(void *priv); - void (*notify_signal_change)(void *priv, int above); + void (*notify_signal_change)(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate); }; #ifdef CONFIG_BGSCAN int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void bgscan_deinit(struct wpa_supplicant *wpa_s); -int bgscan_notify_scan(struct wpa_supplicant *wpa_s); +int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s); -void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above); +void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, + int current_signal, int current_noise, + int current_txrate); #else /* CONFIG_BGSCAN */ @@ -50,7 +56,8 @@ static inline void bgscan_deinit(struct wpa_supplicant *wpa_s) { } -static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s) +static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) { return 0; } @@ -60,7 +67,9 @@ static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s) } static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, - int above) + int above, int current_signal, + int current_noise, + int current_txrate) { } diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c new file mode 100644 index 0000000..5385cce --- /dev/null +++ b/wpa_supplicant/bgscan_learn.c @@ -0,0 +1,610 @@ +/* + * WPA Supplicant - background scan and roaming module: learn + * Copyright (c) 2009-2010, Jouni Malinen + * + * 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 "eloop.h" +#include "list.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "config_ssid.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" +#include "bgscan.h" + +struct bgscan_learn_bss { + struct dl_list list; + u8 bssid[ETH_ALEN]; + int freq; + u8 *neigh; /* num_neigh * ETH_ALEN buffer */ + size_t num_neigh; +}; + +struct bgscan_learn_data { + struct wpa_supplicant *wpa_s; + const struct wpa_ssid *ssid; + int scan_interval; + int signal_threshold; + int short_interval; /* use if signal < threshold */ + int long_interval; /* use if signal > threshold */ + struct os_time last_bgscan; + char *fname; + struct dl_list bss; + int *supp_freqs; + int probe_idx; +}; + + +static void bss_free(struct bgscan_learn_bss *bss) +{ + os_free(bss->neigh); + os_free(bss); +} + + +static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid) +{ + size_t i; + + if (array == NULL || array_len == 0) + return 0; + + for (i = 0; i < array_len; i++) { + if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss, + const u8 *bssid) +{ + u8 *n; + + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return; + if (bssid_in_array(bss->neigh, bss->num_neigh, bssid)) + return; + + n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN); + if (n == NULL) + return; + + os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN); + bss->neigh = n; + bss->num_neigh++; +} + + +static struct bgscan_learn_bss * bgscan_learn_get_bss( + struct bgscan_learn_data *data, const u8 *bssid) +{ + struct bgscan_learn_bss *bss; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + return bss; + } + return NULL; +} + + +static int bgscan_learn_load(struct bgscan_learn_data *data) +{ + FILE *f; + char buf[128]; + struct bgscan_learn_bss *bss; + + if (data->fname == NULL) + return 0; + + f = fopen(data->fname, "r"); + if (f == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s", + data->fname); + + if (fgets(buf, sizeof(buf), f) == NULL || + os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) { + wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s", + data->fname); + fclose(f); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + if (os_strncmp(buf, "BSS ", 4) == 0) { + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + if (hwaddr_aton(buf + 4, bss->bssid) < 0) { + bss_free(bss); + continue; + } + bss->freq = atoi(buf + 4 + 18); + dl_list_add(&data->bss, &bss->list); + wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS " + "entry: " MACSTR " freq=%d", + MAC2STR(bss->bssid), bss->freq); + } + + if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) { + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(buf + 9, addr) < 0) + continue; + bss = bgscan_learn_get_bss(data, addr); + if (bss == NULL) + continue; + if (hwaddr_aton(buf + 9 + 18, addr) < 0) + continue; + + bgscan_learn_add_neighbor(bss, addr); + } + } + + fclose(f); + return 0; +} + + +static void bgscan_learn_save(struct bgscan_learn_data *data) +{ + FILE *f; + struct bgscan_learn_bss *bss; + + if (data->fname == NULL) + return; + + wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s", + data->fname); + + f = fopen(data->fname, "w"); + if (f == NULL) + return; + fprintf(f, "wpa_supplicant-bgscan-learn\n"); + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + fprintf(f, "BSS " MACSTR " %d\n", + MAC2STR(bss->bssid), bss->freq); + } + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + size_t i; + for (i = 0; i < bss->num_neigh; i++) { + fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n", + MAC2STR(bss->bssid), + MAC2STR(bss->neigh + i * ETH_ALEN)); + } + } + + fclose(f); +} + + +static int in_array(int *array, int val) +{ + int i; + + if (array == NULL) + return 0; + + for (i = 0; array[i]; i++) { + if (array[i] == val) + return 1; + } + + return 0; +} + + +static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data, + size_t *count) +{ + struct bgscan_learn_bss *bss; + int *freqs = NULL, *n; + + *count = 0; + + dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { + if (in_array(freqs, bss->freq)) + continue; + n = os_realloc(freqs, (*count + 2) * sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[*count] = bss->freq; + (*count)++; + freqs[*count] = 0; + } + + return freqs; +} + + +static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, + int *freqs, size_t count) +{ + int idx, *n; + + if (data->supp_freqs == NULL) + return freqs; + + idx = data->probe_idx + 1; + while (idx != data->probe_idx) { + if (data->supp_freqs[idx] == 0) + idx = 0; + if (!in_array(freqs, data->supp_freqs[idx])) { + wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq " + "%u", data->supp_freqs[idx]); + data->probe_idx = idx; + n = os_realloc(freqs, (count + 2) * sizeof(int)); + if (n == NULL) + return freqs; + freqs = n; + freqs[count] = data->supp_freqs[idx]; + count++; + freqs[count] = 0; + break; + } + + idx++; + } + + return freqs; +} + + +static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct bgscan_learn_data *data = eloop_ctx; + struct wpa_supplicant *wpa_s = data->wpa_s; + struct wpa_driver_scan_params params; + int *freqs = NULL; + size_t count, i; + char msg[100], *pos; + + os_memset(¶ms, 0, sizeof(params)); + params.num_ssids = 1; + params.ssids[0].ssid = data->ssid->ssid; + params.ssids[0].ssid_len = data->ssid->ssid_len; + if (data->ssid->scan_freq) + params.freqs = data->ssid->scan_freq; + else { + freqs = bgscan_learn_get_freqs(data, &count); + wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have " + "been seen on %u channels", (unsigned int) count); + freqs = bgscan_learn_get_probe_freq(data, freqs, count); + + msg[0] = '\0'; + pos = msg; + for (i = 0; freqs && freqs[i]; i++) { + int ret; + ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d", + freqs[i]); + if (ret < 0 || ret >= msg + sizeof(msg) - pos) + break; + pos += ret; + } + pos[0] = '\0'; + wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s", + msg); + params.freqs = freqs; + } + + wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan"); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan"); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else + os_get_time(&data->last_bgscan); + os_free(freqs); +} + + +static int bgscan_learn_get_params(struct bgscan_learn_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return 0; + + data->short_interval = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return 0; + pos++; + data->signal_threshold = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval " + "for high signal"); + return -1; + } + pos++; + data->long_interval = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos) { + pos++; + data->fname = os_strdup(pos); + } + + return 0; +} + + +static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s) +{ + struct hostapd_hw_modes *modes; + int i, j, *freqs = NULL, *n; + size_t count = 0; + + modes = wpa_s->hw.modes; + if (modes == NULL) + return NULL; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + n = os_realloc(freqs, (count + 2) * sizeof(int)); + if (n == NULL) + continue; + + freqs = n; + freqs[count] = modes[i].channels[j].freq; + count++; + freqs[count] = 0; + } + } + + return freqs; +} + + +static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, + const char *params, + const struct wpa_ssid *ssid) +{ + struct bgscan_learn_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + dl_list_init(&data->bss); + data->wpa_s = wpa_s; + data->ssid = ssid; + if (bgscan_learn_get_params(data, params) < 0) { + os_free(data->fname); + os_free(data); + return NULL; + } + if (data->short_interval <= 0) + data->short_interval = 30; + if (data->long_interval <= 0) + data->long_interval = 30; + + if (bgscan_learn_load(data) < 0) { + os_free(data->fname); + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d " + "Short bgscan interval %d Long bgscan interval %d", + data->signal_threshold, data->short_interval, + data->long_interval); + + if (data->signal_threshold && + wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) { + wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable " + "signal strength monitoring"); + } + + data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s); + data->scan_interval = data->short_interval; + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + + /* + * This function is called immediately after an association, so it is + * reasonable to assume that a scan was completed recently. This makes + * us skip an immediate new scan in cases where the current signal + * level is below the bgscan threshold. + */ + os_get_time(&data->last_bgscan); + + return data; +} + + +static void bgscan_learn_deinit(void *priv) +{ + struct bgscan_learn_data *data = priv; + struct bgscan_learn_bss *bss, *n; + + bgscan_learn_save(data); + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + if (data->signal_threshold) + wpa_drv_signal_monitor(data->wpa_s, 0, 0); + os_free(data->fname); + dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss, + list) { + dl_list_del(&bss->list); + bss_free(bss); + } + os_free(data->supp_freqs); + os_free(data); +} + + +static int bgscan_learn_bss_match(struct bgscan_learn_data *data, + struct wpa_scan_res *bss) +{ + const u8 *ie; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + return 0; + + if (data->ssid->ssid_len != ie[1] || + os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0) + return 0; /* SSID mismatch */ + + return 1; +} + + +static int bgscan_learn_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct bgscan_learn_data *data = priv; + size_t i, j; +#define MAX_BSS 50 + u8 bssid[MAX_BSS * ETH_ALEN]; + size_t num_bssid = 0; + + wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification"); + + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, + data, NULL); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + if (!bgscan_learn_bss_match(data, res)) + continue; + + if (num_bssid < MAX_BSS) { + os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid, + ETH_ALEN); + num_bssid++; + } + } + wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan " + "results", (unsigned int) num_bssid); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + struct bgscan_learn_bss *bss; + + if (!bgscan_learn_bss_match(data, res)) + continue; + + bss = bgscan_learn_get_bss(data, res->bssid); + if (bss && bss->freq != res->freq) { + wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS " + MACSTR " freq %d -> %d", + MAC2STR(res->bssid), bss->freq, res->freq); + bss->freq = res->freq; + } else if (!bss) { + wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR + " freq=%d", MAC2STR(res->bssid), res->freq); + bss = os_zalloc(sizeof(*bss)); + if (!bss) + continue; + os_memcpy(bss->bssid, res->bssid, ETH_ALEN); + bss->freq = res->freq; + dl_list_add(&data->bss, &bss->list); + } + + for (j = 0; j < num_bssid; j++) { + u8 *addr = bssid + j * ETH_ALEN; + bgscan_learn_add_neighbor(bss, addr); + } + } + + /* + * A more advanced bgscan could process scan results internally, select + * the BSS and request roam if needed. This sample uses the existing + * BSS/ESS selection routine. Change this to return 1 if selection is + * done inside the bgscan module. + */ + + return 0; +} + + +static void bgscan_learn_notify_beacon_loss(void *priv) +{ + wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss"); + /* TODO: speed up background scanning */ +} + + +static void bgscan_learn_notify_signal_change(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate) +{ + struct bgscan_learn_data *data = priv; + int scan = 0; + struct os_time now; + + if (data->short_interval == data->long_interval || + data->signal_threshold == 0) + return; + + wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed " + "(above=%d current_signal=%d current_noise=%d " + "current_txrate=%d)", above, current_signal, + current_noise, current_txrate); + if (data->scan_interval == data->long_interval && !above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan " + "interval"); + data->scan_interval = data->short_interval; + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 1) + scan = 1; + } else if (data->scan_interval == data->short_interval && above) { + wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan " + "interval"); + data->scan_interval = data->long_interval; + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_learn_timeout, data, NULL); + } else if (!above) { + /* + * Signal dropped further 4 dB. Request a new scan if we have + * not yet scanned in a while. + */ + os_get_time(&now); + if (now.sec > data->last_bgscan.sec + 10) + scan = 1; + } + + if (scan) { + wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan"); + eloop_cancel_timeout(bgscan_learn_timeout, data, NULL); + eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL); + } +} + + +const struct bgscan_ops bgscan_learn_ops = { + .name = "learn", + .init = bgscan_learn_init, + .deinit = bgscan_learn_deinit, + .notify_scan = bgscan_learn_notify_scan, + .notify_beacon_loss = bgscan_learn_notify_beacon_loss, + .notify_signal_change = bgscan_learn_notify_signal_change, +}; diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c index 8e80b12..eedc961 100644 --- a/wpa_supplicant/bgscan_simple.c +++ b/wpa_supplicant/bgscan_simple.c @@ -28,6 +28,7 @@ struct bgscan_simple_data { const struct wpa_ssid *ssid; int scan_interval; int signal_threshold; + int short_scan_count; /* counter for scans using short scan interval */ int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ struct os_time last_bgscan; @@ -57,8 +58,23 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan"); eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, data, NULL); - } else + } else { + if (data->scan_interval == data->short_interval) { + data->short_scan_count++; + /* + * Spend at most the duration of a long scan interval + * scanning at the short scan interval. After that, + * revert to the long scan interval. + */ + if (data->short_scan_count > + data->long_interval / data->short_interval + 1) { + data->scan_interval = data->long_interval; + wpa_printf(MSG_DEBUG, "bgscan simple: Backing " + "off to long scan interval"); + } + } os_get_time(&data->last_bgscan); + } } @@ -122,6 +138,15 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, } data->scan_interval = data->short_interval; + if (data->signal_threshold) { + /* Poll for signal info to set initial scan interval */ + struct wpa_signal_info siginfo; + if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 && + siginfo.current_signal >= data->signal_threshold) + data->scan_interval = data->long_interval; + } + wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d", + data->scan_interval); eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout, data, NULL); @@ -147,7 +172,8 @@ static void bgscan_simple_deinit(void *priv) } -static int bgscan_simple_notify_scan(void *priv) +static int bgscan_simple_notify_scan(void *priv, + struct wpa_scan_results *scan_res) { struct bgscan_simple_data *data = priv; @@ -175,7 +201,10 @@ static void bgscan_simple_notify_beacon_loss(void *priv) } -static void bgscan_simple_notify_signal_change(void *priv, int above) +static void bgscan_simple_notify_signal_change(void *priv, int above, + int current_signal, + int current_noise, + int current_txrate) { struct bgscan_simple_data *data = priv; int scan = 0; @@ -186,14 +215,29 @@ static void bgscan_simple_notify_signal_change(void *priv, int above) return; wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed " - "(above=%d)", above); + "(above=%d current_signal=%d current_noise=%d " + "current_txrate=%d))", above, current_signal, + current_noise, current_txrate); if (data->scan_interval == data->long_interval && !above) { wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " "bgscan interval"); data->scan_interval = data->short_interval; + data->short_scan_count = 0; os_get_time(&now); if (now.sec > data->last_bgscan.sec + 1) scan = 1; + else if (data->last_bgscan.sec + data->long_interval > + now.sec + data->scan_interval) { + /* + * Restart scan interval timer if currently scheduled + * scan is too far in the future. + */ + eloop_cancel_timeout(bgscan_simple_timeout, data, + NULL); + eloop_register_timeout(data->scan_interval, 0, + bgscan_simple_timeout, data, + NULL); + } } else if (data->scan_interval == data->short_interval && above) { wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan " "interval"); diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c index 4ffb220..8f12ac9 100644 --- a/wpa_supplicant/blacklist.c +++ b/wpa_supplicant/blacklist.c @@ -44,7 +44,7 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, * wpa_blacklist_add - Add an BSSID to the blacklist * @wpa_s: Pointer to wpa_supplicant data * @bssid: BSSID to be added to the blacklist - * Returns: 0 on success, -1 on failure + * Returns: Current blacklist count on success, -1 on failure * * This function adds the specified BSSID to the blacklist or increases the * blacklist count if the BSSID was already listed. It should be called when @@ -66,7 +66,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count " "incremented to %d", MAC2STR(bssid), e->count); - return 0; + return e->count; } e = os_zalloc(sizeof(*e)); @@ -79,7 +79,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", MAC2STR(bssid)); - return 0; + return e->count; } diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index e2ac230..1b27440 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -30,25 +30,6 @@ */ #define WPA_BSS_EXPIRATION_PERIOD 10 -/** - * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired - * - * This value control the time in seconds after which a BSS entry gets removed - * if it has not been updated or is not in use. - */ -#define WPA_BSS_EXPIRATION_AGE 180 - -/** - * WPA_BSS_EXPIRATION_SCAN_COUNT - Expire BSS after number of scans - * - * If the BSS entry has not been seen in this many scans, it will be removed. - * Value 1 means that the entry is removed after the first scan without the - * BSSID being seen. Larger values can be used to avoid BSS entries - * disappearing if they are not visible in every scan (e.g., low signal quality - * or interference). - */ -#define WPA_BSS_EXPIRATION_SCAN_COUNT 2 - #define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) #define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) #define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) @@ -65,10 +46,19 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) dl_list_del(&bss->list); dl_list_del(&bss->list_id); wpa_s->num_bss--; - wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'", - bss->id, MAC2STR(bss->bssid), - wpa_ssid_txt(bss->ssid, bss->ssid_len)); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR + " SSID '%s'", bss->id, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); +#ifdef CONFIG_INTERWORKING + wpabuf_free(bss->anqp_venue_name); + wpabuf_free(bss->anqp_network_auth_type); + wpabuf_free(bss->anqp_roaming_consortium); + wpabuf_free(bss->anqp_ip_addr_type_availability); + wpabuf_free(bss->anqp_nai_realm); + wpabuf_free(bss->anqp_3gpp); + wpabuf_free(bss->anqp_domain_name); +#endif /* CONFIG_INTERWORKING */ os_free(bss); } @@ -112,6 +102,55 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) } +static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->ssid == NULL || ssid->ssid_len == 0) + continue; + if (ssid->ssid_len == bss->ssid_len && + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0) + return 1; + } + + return 0; +} + + +static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!wpa_bss_known(wpa_s, bss)) { + wpa_bss_remove(wpa_s, bss); + return 0; + } + } + + return -1; +} + + +static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) +{ + /* + * Remove the oldest entry that does not match with any configured + * network. + */ + if (wpa_bss_remove_oldest_unknown(wpa_s) == 0) + return; + + /* + * Remove the oldest entry since no better candidate for removal was + * found. + */ + wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, + struct wpa_bss, list)); +} + + static void wpa_bss_add(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len, struct wpa_scan_res *res) @@ -133,14 +172,12 @@ static void wpa_bss_add(struct wpa_supplicant *wpa_s, dl_list_add_tail(&wpa_s->bss, &bss->list); dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); wpa_s->num_bss++; - wpa_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'", - bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR + " SSID '%s'", + bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); - if (wpa_s->num_bss > wpa_s->conf->bss_max_count) { - /* Remove the oldest entry */ - wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, - struct wpa_bss, list)); - } + if (wpa_s->num_bss > wpa_s->conf->bss_max_count) + wpa_bss_remove_oldest(wpa_s); } @@ -187,8 +224,11 @@ static int are_ies_equal(const struct wpa_bss *old, new_ie_len = new_ie ? new_ie[1] + 2 : 0; } - ret = (old_ie_len == new_ie_len && - os_memcmp(old_ie, new_ie, old_ie_len) == 0); + if (!old_ie || !new_ie) + ret = !old_ie && !new_ie; + else + ret = (old_ie_len == new_ie_len && + os_memcmp(old_ie, new_ie, old_ie_len) == 0); wpabuf_free(old_ie_buff); wpabuf_free(new_ie_buff); @@ -292,6 +332,8 @@ static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (nbss) { + if (wpa_s->current_bss == bss) + wpa_s->current_bss = nbss; bss = nbss; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); @@ -317,29 +359,34 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) void wpa_bss_update_start(struct wpa_supplicant *wpa_s) { wpa_s->bss_update_idx++; - wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u", - wpa_s->bss_update_idx); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u", + wpa_s->bss_update_idx); } void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res) { - const u8 *ssid; + const u8 *ssid, *p2p; struct wpa_bss *bss; ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR, - MAC2STR(res->bssid)); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " + MACSTR, MAC2STR(res->bssid)); return; } if (ssid[1] > 32) { - wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for " - MACSTR, MAC2STR(res->bssid)); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " + MACSTR, MAC2STR(res->bssid)); return; } + p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); + if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && + os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) + return; /* Skip P2P listen discovery results here */ + /* TODO: add option for ignoring BSSes we are not interested in * (to save memory) */ bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); @@ -406,18 +453,18 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, continue; /* expire only BSSes that were scanned */ if (bss->last_update_idx < wpa_s->bss_update_idx) bss->scan_miss_count++; - if (bss->scan_miss_count >= WPA_BSS_EXPIRATION_SCAN_COUNT) { - wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no " - "match in scan", bss->id); + if (bss->scan_miss_count >= + wpa_s->conf->bss_expiration_scan_count) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " + "no match in scan", bss->id); wpa_bss_remove(wpa_s, bss); } } } -static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) { - struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_bss *bss, *n; struct os_time t; @@ -425,19 +472,27 @@ static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) return; os_get_time(&t); - t.sec -= WPA_BSS_EXPIRATION_AGE; + t.sec -= age; dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { if (wpa_bss_in_use(wpa_s, bss)) continue; if (os_time_before(&bss->last_update, &t)) { - wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age", - bss->id); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " + "age", bss->id); wpa_bss_remove(wpa_s, bss); } else break; } +} + + +static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, wpa_bss_timeout, wpa_s, NULL); } @@ -453,14 +508,25 @@ int wpa_bss_init(struct wpa_supplicant *wpa_s) } -void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +void wpa_bss_flush(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *n; - eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); + if (wpa_s->bss.next == NULL) return; /* BSS table not yet initialized */ - dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (wpa_bss_in_use(wpa_s, bss)) + continue; wpa_bss_remove(wpa_s, bss); + } +} + + +void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); + wpa_bss_flush(wpa_s); } @@ -468,7 +534,7 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss *bss; - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) return bss; } @@ -476,6 +542,23 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_P2P +struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + struct wpa_bss *bss; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { + u8 addr[ETH_ALEN]; + if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, + addr) == 0 && + os_memcmp(addr, dev_addr, ETH_ALEN) == 0) + return bss; + } + return NULL; +} +#endif /* CONFIG_P2P */ + + struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) { struct wpa_bss *bss; diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 1de4722..9f13298 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -23,6 +23,7 @@ struct wpa_scan_res; #define WPA_BSS_LEVEL_DBM BIT(3) #define WPA_BSS_AUTHENTICATED BIT(4) #define WPA_BSS_ASSOCIATED BIT(5) +#define WPA_BSS_ANQP_FETCH_TRIED BIT(6) /** * struct wpa_bss - BSS table @@ -65,6 +66,15 @@ struct wpa_bss { int level; u64 tsf; struct os_time last_update; +#ifdef CONFIG_INTERWORKING + struct wpabuf *anqp_venue_name; + struct wpabuf *anqp_network_auth_type; + struct wpabuf *anqp_roaming_consortium; + struct wpabuf *anqp_ip_addr_type_availability; + struct wpabuf *anqp_nai_realm; + struct wpabuf *anqp_3gpp; + struct wpabuf *anqp_domain_name; +#endif /* CONFIG_INTERWORKING */ size_t ie_len; size_t beacon_ie_len; /* followed by ie_len octets of IEs */ @@ -78,10 +88,14 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan); int wpa_bss_init(struct wpa_supplicant *wpa_s); void wpa_bss_deinit(struct wpa_supplicant *wpa_s); +void wpa_bss_flush(struct wpa_supplicant *wpa_s); +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age); struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid, size_t ssid_len); struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); +struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id); const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie); const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 7e2a5b4..600e32b 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -15,9 +15,11 @@ #include "includes.h" #include "common.h" +#include "utils/uuid.h" #include "crypto/sha1.h" #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" +#include "p2p/p2p.h" #include "config.h" @@ -283,6 +285,12 @@ static int wpa_config_parse_bssid(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { + if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 || + os_strcmp(value, "any") == 0) { + ssid->bssid_set = 0; + wpa_printf(MSG_MSGDUMP, "BSSID any"); + return 0; + } if (hwaddr_aton(value, ssid->bssid)) { wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", line, value); @@ -1109,6 +1117,7 @@ static int wpa_config_parse_eap(const struct parse_data *data, wpa_hexdump(MSG_MSGDUMP, "eap methods", (u8 *) methods, num_methods * sizeof(*methods)); + os_free(ssid->eap.eap_methods); ssid->eap.eap_methods = methods; return errors ? -1 : 0; } @@ -1344,6 +1353,90 @@ static char * wpa_config_write_wep_key3(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +#ifdef CONFIG_P2P + +static int wpa_config_parse_p2p_client_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + const char *pos; + u8 *buf, *n, addr[ETH_ALEN]; + size_t count; + + buf = NULL; + count = 0; + + pos = value; + while (pos && *pos) { + while (*pos == ' ') + pos++; + + if (hwaddr_aton(pos, addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "p2p_client_list address '%s'.", + line, value); + /* continue anyway */ + } else { + n = os_realloc(buf, (count + 1) * ETH_ALEN); + if (n == NULL) { + os_free(buf); + return -1; + } + buf = n; + os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN); + count++; + wpa_hexdump(MSG_MSGDUMP, "p2p_client_list", + addr, ETH_ALEN); + } + + pos = os_strchr(pos, ' '); + } + + os_free(ssid->p2p_client_list); + ssid->p2p_client_list = buf; + ssid->num_p2p_clients = count; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_p2p_client_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value, *end, *pos; + int res; + size_t i; + + if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0) + return NULL; + + value = os_malloc(20 * ssid->num_p2p_clients); + if (value == NULL) + return NULL; + pos = value; + end = value + 20 * ssid->num_p2p_clients; + + for (i = 0; i < ssid->num_p2p_clients; i++) { + res = os_snprintf(pos, end - pos, MACSTR " ", + MAC2STR(ssid->p2p_client_list + + i * ETH_ALEN)); + if (res < 0 || res >= end - pos) { + os_free(value); + return NULL; + } + pos += res; + } + + if (pos > value) + pos[-1] = '\0'; + + return value; +} +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_P2P */ + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1492,9 +1585,9 @@ static const struct parse_data ssid_fields[] = { { STRe(pac_file) }, { INTe(fragment_size) }, #endif /* IEEE8021X_EAPOL */ - { INT_RANGE(mode, 0, 2) }, + { INT_RANGE(mode, 0, 4) }, { INT_RANGE(proactive_key_caching, 0, 1) }, - { INT_RANGE(disabled, 0, 1) }, + { INT_RANGE(disabled, 0, 2) }, { STR(id_str) }, #ifdef CONFIG_IEEE80211W { INT_RANGE(ieee80211w, 0, 2) }, @@ -1504,6 +1597,9 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(frequency, 0, 10000) }, { INT(wpa_ptk_rekey) }, { STR(bgscan) }, +#ifdef CONFIG_P2P + { FUNC(p2p_client_list) }, +#endif /* CONFIG_P2P */ }; #undef OFFSET @@ -1670,6 +1766,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->scan_freq); os_free(ssid->freq_list); os_free(ssid->bgscan); + os_free(ssid->p2p_client_list); os_free(ssid); } @@ -1687,6 +1784,7 @@ void wpa_config_free(struct wpa_config *config) struct wpa_config_blob *blob, *prevblob; #endif /* CONFIG_NO_CONFIG_BLOBS */ struct wpa_ssid *ssid, *prev = NULL; + ssid = config->ssid; while (ssid) { prev = ssid; @@ -1715,14 +1813,46 @@ void wpa_config_free(struct wpa_config *config) os_free(config->model_name); os_free(config->model_number); os_free(config->serial_number); - os_free(config->device_type); os_free(config->config_methods); + os_free(config->p2p_ssid_postfix); os_free(config->pssid); + os_free(config->home_realm); + os_free(config->home_username); + os_free(config->home_password); + os_free(config->home_ca_cert); + os_free(config->home_imsi); + os_free(config->home_milenage); + os_free(config->p2p_pref_chan); os_free(config); } /** + * wpa_config_foreach_network - Iterate over each configured network + * @config: Configuration data from wpa_config_read() + * @func: Callback function to process each network + * @arg: Opaque argument to pass to callback function + * + * Iterate over the set of configured networks calling the specified + * function for each item. We guard against callbacks removing the + * supplied network. + */ +void wpa_config_foreach_network(struct wpa_config *config, + void (*func)(void *, struct wpa_ssid *), + void *arg) +{ + struct wpa_ssid *ssid, *next; + + ssid = config->ssid; + while (ssid) { + next = ssid->next; + func(arg, ssid); + ssid = next; + } +} + + +/** * wpa_config_get_network - Get configured network based on id * @config: Configuration data from wpa_config_read() * @id: Unique network id to search for @@ -1876,10 +2006,32 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, } +int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var, + const char *value) +{ + size_t len; + char *buf; + int ret; + + len = os_strlen(value); + buf = os_malloc(len + 3); + if (buf == NULL) + return -1; + buf[0] = '"'; + os_memcpy(buf + 1, value, len); + buf[len + 1] = '"'; + buf[len + 2] = '\0'; + ret = wpa_config_set(ssid, var, buf, 0); + os_free(buf); + return ret; +} + + /** * wpa_config_get_all - Get all options from network configuration * @ssid: Pointer to network configuration data * @get_keys: Determines if keys/passwords will be included in returned list + * (if they may be exported) * Returns: %NULL terminated list of all set keys and their values in the form * of [key1, val1, key2, val2, ... , NULL] * @@ -1895,6 +2047,8 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) char **props; int fields_num; + get_keys = get_keys && ssid->export_keys; + props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1)); if (!props) return NULL; @@ -2131,7 +2285,13 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->eapol_version = DEFAULT_EAPOL_VERSION; config->ap_scan = DEFAULT_AP_SCAN; config->fast_reauth = DEFAULT_FAST_REAUTH; + config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; + config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; config->bss_max_count = DEFAULT_BSS_MAX_COUNT; + config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE; + config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT; + config->max_num_sta = DEFAULT_MAX_NUM_STA; + config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -2165,3 +2325,358 @@ void wpa_config_debug_dump_networks(struct wpa_config *config) } } #endif /* CONFIG_NO_STDOUT_DEBUG */ + + +struct global_parse_data { + char *name; + int (*parser)(const struct global_parse_data *data, + struct wpa_config *config, int line, const char *value); + void *param1, *param2, *param3; + unsigned int changed_flag; +}; + + +static int wpa_global_config_parse_int(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + int *dst; + dst = (int *) (((u8 *) config) + (long) data->param1); + *dst = atoi(pos); + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); + + if (data->param2 && *dst < (long) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param2); + *dst = (long) data->param2; + return -1; + } + + if (data->param3 && *dst > (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + return 0; +} + + +static int wpa_global_config_parse_str(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + char **dst, *tmp; + + len = os_strlen(pos); + if (data->param2 && len < (size_t) data->param2) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param2); + return -1; + } + + if (data->param3 && len > (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) len, (long) data->param3); + return -1; + } + + tmp = os_strdup(pos); + if (tmp == NULL) + return -1; + + dst = (char **) (((u8 *) config) + (long) data->param1); + os_free(*dst); + *dst = tmp; + wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst); + + return 0; +} + + +static int wpa_config_process_country(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (!pos[0] || !pos[1]) { + wpa_printf(MSG_DEBUG, "Invalid country set"); + return -1; + } + config->country[0] = pos[0]; + config->country[1] = pos[1]; + wpa_printf(MSG_DEBUG, "country='%c%c'", + config->country[0], config->country[1]); + return 0; +} + + +static int wpa_config_process_load_dynamic_eap( + const struct global_parse_data *data, struct wpa_config *config, + int line, const char *so) +{ + int ret; + wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); + ret = eap_peer_method_load(so); + if (ret == -2) { + wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " + "reloading."); + } else if (ret) { + wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " + "method '%s'.", line, so); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_WPS + +static int wpa_config_process_uuid(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + char buf[40]; + if (uuid_str2bin(pos, config->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); + return -1; + } + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "uuid=%s", buf); + return 0; +} + + +static int wpa_config_process_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + return wps_dev_type_str2bin(pos, config->device_type); +} + + +static int wpa_config_process_os_version(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (hexstr2bin(pos, config->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); + return -1; + } + wpa_printf(MSG_DEBUG, "os_version=%08x", + WPA_GET_BE32(config->os_version)); + return 0; +} + +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P +static int wpa_config_process_sec_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int idx; + + if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) { + wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type " + "items", line); + return -1; + } + + idx = config->num_sec_device_types; + + if (wps_dev_type_str2bin(pos, config->sec_device_type[idx])) + return -1; + + config->num_sec_device_types++; + return 0; +} + + +static int wpa_config_process_p2p_pref_chan( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct p2p_channel *pref = NULL, *n; + unsigned int num = 0; + const char *pos2; + u8 op_class, chan; + + /* format: class:chan,class:chan,... */ + + while (*pos) { + op_class = atoi(pos); + pos2 = os_strchr(pos, ':'); + if (pos2 == NULL) + goto fail; + pos2++; + chan = atoi(pos2); + + n = os_realloc(pref, (num + 1) * sizeof(struct p2p_channel)); + if (n == NULL) + goto fail; + pref = n; + pref[num].op_class = op_class; + pref[num].chan = chan; + num++; + + pos = os_strchr(pos2, ','); + if (pos == NULL) + break; + pos++; + } + + os_free(config->p2p_pref_chan); + config->p2p_pref_chan = pref; + config->num_p2p_pref_chan = num; + wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs", + (u8 *) config->p2p_pref_chan, + config->num_p2p_pref_chan * sizeof(struct p2p_channel)); + + return 0; + +fail: + os_free(pref); + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line); + return -1; +} +#endif /* CONFIG_P2P */ + + +static int wpa_config_process_hessid( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + if (hwaddr_aton2(pos, config->hessid) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'", + line, pos); + return -1; + } + + return 0; +} + + +#ifdef OFFSET +#undef OFFSET +#endif /* OFFSET */ +/* OFFSET: Get offset of a variable within the wpa_config structure */ +#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) + +#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL +#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL +#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f) +#define INT(f) _INT(f), NULL, NULL +#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max +#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f) +#define STR(f) _STR(f), NULL, NULL +#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max + +static const struct global_parse_data global_fields[] = { +#ifdef CONFIG_CTRL_IFACE + { STR(ctrl_interface), 0 }, + { STR(ctrl_interface_group), 0 } /* deprecated */, +#endif /* CONFIG_CTRL_IFACE */ + { INT_RANGE(eapol_version, 1, 2), 0 }, + { INT(ap_scan), 0 }, + { INT(fast_reauth), 0 }, + { STR(opensc_engine_path), 0 }, + { STR(pkcs11_engine_path), 0 }, + { STR(pkcs11_module_path), 0 }, + { STR(driver_param), 0 }, + { INT(dot11RSNAConfigPMKLifetime), 0 }, + { INT(dot11RSNAConfigPMKReauthThreshold), 0 }, + { INT(dot11RSNAConfigSATimeout), 0 }, +#ifndef CONFIG_NO_CONFIG_WRITE + { INT(update_config), 0 }, +#endif /* CONFIG_NO_CONFIG_WRITE */ + { FUNC_NO_VAR(load_dynamic_eap), 0 }, +#ifdef CONFIG_WPS + { FUNC(uuid), CFG_CHANGED_UUID }, + { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME }, + { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING }, + { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING }, + { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE }, + { FUNC(os_version), CFG_CHANGED_OS_VERSION }, + { STR(config_methods), CFG_CHANGED_CONFIG_METHODS }, + { INT_RANGE(wps_cred_processing, 0, 2), 0 }, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE }, + { INT(p2p_listen_reg_class), 0 }, + { INT(p2p_listen_channel), 0 }, + { INT(p2p_oper_reg_class), 0 }, + { INT(p2p_oper_channel), 0 }, + { INT_RANGE(p2p_go_intent, 0, 15), 0 }, + { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX }, + { INT_RANGE(persistent_reconnect, 0, 1), 0 }, + { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS }, + { INT(p2p_group_idle), 0 }, + { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, +#endif /* CONFIG_P2P */ + { FUNC(country), CFG_CHANGED_COUNTRY }, + { INT(bss_max_count), 0 }, + { INT(bss_expiration_age), 0 }, + { INT(bss_expiration_scan_count), 0 }, + { INT_RANGE(filter_ssids, 0, 1), 0 }, + { INT(max_num_sta), 0 }, + { INT_RANGE(disassoc_low_ack, 0, 1), 0 }, + { STR(home_realm), 0 }, + { STR(home_username), 0 }, + { STR(home_password), 0 }, + { STR(home_ca_cert), 0 }, + { STR(home_imsi), 0 }, + { STR(home_milenage), 0 }, + { INT_RANGE(interworking, 0, 1), 0 }, + { FUNC(hessid), 0 }, + { INT_RANGE(access_network_type, 0, 15), 0 } +}; + +#undef FUNC +#undef _INT +#undef INT +#undef INT_RANGE +#undef _STR +#undef STR +#undef STR_RANGE +#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) + + +int wpa_config_process_global(struct wpa_config *config, char *pos, int line) +{ + size_t i; + int ret = 0; + + for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { + const struct global_parse_data *field = &global_fields[i]; + size_t flen = os_strlen(field->name); + if (os_strncmp(pos, field->name, flen) != 0 || + pos[flen] != '=') + continue; + + if (field->parser(field, config, line, pos + flen + 1)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse '%s'.", line, pos); + ret = -1; + } + config->changed_parameters |= field->changed_flag; + break; + } + if (i == NUM_GLOBAL_FIELDS) { + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", + line, pos); + ret = -1; + } + + return ret; +} diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 754e4be..18317f5 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -22,10 +22,32 @@ #define DEFAULT_AP_SCAN 1 #endif /* CONFIG_NO_SCAN_PROCESSING */ #define DEFAULT_FAST_REAUTH 1 +#define DEFAULT_P2P_GO_INTENT 7 +#define DEFAULT_P2P_INTRA_BSS 1 #define DEFAULT_BSS_MAX_COUNT 200 +#define DEFAULT_BSS_EXPIRATION_AGE 180 +#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 +#define DEFAULT_MAX_NUM_STA 128 +#define DEFAULT_ACCESS_NETWORK_TYPE 15 #include "config_ssid.h" - +#include "wps/wps.h" + + +#define CFG_CHANGED_DEVICE_NAME BIT(0) +#define CFG_CHANGED_CONFIG_METHODS BIT(1) +#define CFG_CHANGED_DEVICE_TYPE BIT(2) +#define CFG_CHANGED_OS_VERSION BIT(3) +#define CFG_CHANGED_UUID BIT(4) +#define CFG_CHANGED_COUNTRY BIT(5) +#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6) +#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7) +#define CFG_CHANGED_WPS_STRING BIT(8) +#define CFG_CHANGED_P2P_INTRA_BSS BIT(9) +#define CFG_CHANGED_VENDOR_EXTENSION BIT(10) +#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11) +#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12) +#define CFG_CHANGED_P2P_PREF_CHAN BIT(13) /** * struct wpa_config - wpa_supplicant configuration data @@ -103,7 +125,7 @@ struct wpa_config { * If this is specified, %wpa_supplicant will open a control interface * that is available for external programs to manage %wpa_supplicant. * The meaning of this string depends on which control interface - * mechanism is used. For all cases, the existance of this parameter + * mechanism is used. For all cases, the existence of this parameter * in configuration is used to determine whether the control interface * is enabled. * @@ -285,26 +307,19 @@ struct wpa_config { /** * device_type - Primary Device Type (WPS) - * Used format: categ-OUI-subcateg - * categ = Category as an integer value - * OUI = OUI and type octet as a 4-octet hex-encoded value; - * 0050F204 for default WPS OUI - * subcateg = OUI-specific Sub Category as an integer value - * Examples: - * 1-0050F204-1 (Computer / PC) - * 1-0050F204-2 (Computer / Server) - * 5-0050F204-1 (Storage / NAS) - * 6-0050F204-1 (Network Infrastructure / AP) */ - char *device_type; + u8 device_type[WPS_DEV_TYPE_LEN]; /** * config_methods - Config Methods * * This is a space-separated list of supported WPS configuration - * methods. For example, "label display push_button keypad". + * methods. For example, "label virtual_display virtual_push_button + * keypad". * Available methods: usba ethernet label display ext_nfc_token - * int_nfc_token nfc_interface push_button keypad. + * int_nfc_token nfc_interface push_button keypad + * virtual_display physical_display + * virtual_push_button physical_push_button. */ char *config_methods; @@ -333,18 +348,145 @@ struct wpa_config { */ int wps_cred_processing; +#define MAX_SEC_DEVICE_TYPES 5 + /** + * sec_device_types - Secondary Device Types (P2P) + */ + u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN]; + int num_sec_device_types; + + int p2p_listen_reg_class; + int p2p_listen_channel; + int p2p_oper_reg_class; + int p2p_oper_channel; + int p2p_go_intent; + char *p2p_ssid_postfix; + int persistent_reconnect; + int p2p_intra_bss; + unsigned int num_p2p_pref_chan; + struct p2p_channel *p2p_pref_chan; + +#define MAX_WPS_VENDOR_EXT 10 + /** + * wps_vendor_ext - Vendor extension attributes in WPS + */ + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT]; + + /** + * p2p_group_idle - Maximum idle time in seconds for P2P group + * + * This value controls how long a P2P group is maintained after there + * is no other members in the group. As a GO, this means no associated + * stations in the group. As a P2P client, this means no GO seen in + * scan results. The maximum idle time is specified in seconds with 0 + * indicating no time limit, i.e., the P2P group remains in active + * state indefinitely until explicitly removed. As a P2P client, the + * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e., + * this parameter is mainly meant for GO use and for P2P client, it can + * only be used to reduce the default timeout to smaller value. + */ + unsigned int p2p_group_idle; + /** * bss_max_count - Maximum number of BSS entries to keep in memory */ unsigned int bss_max_count; /** + * bss_expiration_age - BSS entry age after which it can be expired + * + * This value controls the time in seconds after which a BSS entry + * gets removed if it has not been updated or is not in use. + */ + unsigned int bss_expiration_age; + + /** + * bss_expiration_scan_count - Expire BSS after number of scans + * + * If the BSS entry has not been seen in this many scans, it will be + * removed. A value of 1 means that entry is removed after the first + * scan in which the BSSID is not seen. Larger values can be used + * to avoid BSS entries disappearing if they are not visible in + * every scan (e.g., low signal quality or interference). + */ + unsigned int bss_expiration_scan_count; + + /** * filter_ssids - SSID-based scan result filtering * * 0 = do not filter scan results * 1 = only include configured SSIDs in scan results/BSS table */ int filter_ssids; + + /** + * max_num_sta - Maximum number of STAs in an AP/P2P GO + */ + unsigned int max_num_sta; + + /** + * changed_parameters - Bitmap of changed parameters since last update + */ + unsigned int changed_parameters; + + /** + * disassoc_low_ack - Disassocicate stations with massive packet loss + */ + int disassoc_low_ack; + + /** + * interworking - Whether Interworking (IEEE 802.11u) is enabled + */ + int interworking; + + /** + * access_network_type - Access Network Type + * + * When Interworking is enabled, scans will be limited to APs that + * advertise the specified Access Network Type (0..15; with 15 + * indicating wildcard match). + */ + int access_network_type; + + /** + * hessid - Homogenous ESS identifier + * + * If this is set (any octet is non-zero), scans will be used to + * request response only from BSSes belonging to the specified + * Homogeneous ESS. This is used only if interworking is enabled. + */ + u8 hessid[ETH_ALEN]; + + /** + * home_realm - Home Realm for Interworking + */ + char *home_realm; + + /** + * home_username - Username for Interworking network selection + */ + char *home_username; + + /** + * home_password - Password for Interworking network selection + */ + char *home_password; + + /** + * home_ca_cert - CA certificate for Interworking network selection + */ + char *home_ca_cert; + + /** + * home_imsi - IMSI in | | '-' | format + */ + char *home_imsi; + + /** + * home_milenage - Milenage parameters for SIM/USIM simulator in + * :: format + */ + char *home_milenage; }; @@ -352,12 +494,17 @@ struct wpa_config { void wpa_config_free(struct wpa_config *ssid); void wpa_config_free_ssid(struct wpa_ssid *ssid); +void wpa_config_foreach_network(struct wpa_config *config, + void (*func)(void *, struct wpa_ssid *), + void *arg); struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id); struct wpa_ssid * wpa_config_add_network(struct wpa_config *config); int wpa_config_remove_network(struct wpa_config *config, int id); void wpa_config_set_network_defaults(struct wpa_ssid *ssid); int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, int line); +int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var, + const char *value); char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys); char * wpa_config_get(struct wpa_ssid *ssid, const char *var); char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var); @@ -381,6 +528,10 @@ void wpa_config_debug_dump_networks(struct wpa_config *config); #endif /* CONFIG_NO_STDOUT_DEBUG */ +/* Prototypes for common functions from config.c */ +int wpa_config_process_global(struct wpa_config *config, char *pos, int line); + + /* Prototypes for backend specific functions from the selected config_*.c */ /** diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 5f07045..c1075e3 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -22,7 +22,7 @@ #include "config.h" #include "base64.h" #include "uuid.h" -#include "eap_peer/eap_methods.h" +#include "p2p/p2p.h" /** @@ -270,238 +270,6 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f, #endif /* CONFIG_NO_CONFIG_BLOBS */ -struct global_parse_data { - char *name; - int (*parser)(const struct global_parse_data *data, - struct wpa_config *config, int line, const char *value); - void *param1, *param2, *param3; -}; - - -static int wpa_config_parse_int(const struct global_parse_data *data, - struct wpa_config *config, int line, - const char *pos) -{ - int *dst; - dst = (int *) (((u8 *) config) + (long) data->param1); - *dst = atoi(pos); - wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); - - if (data->param2 && *dst < (long) data->param2) { - wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " - "min_value=%ld)", line, data->name, *dst, - (long) data->param2); - *dst = (long) data->param2; - return -1; - } - - if (data->param3 && *dst > (long) data->param3) { - wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " - "max_value=%ld)", line, data->name, *dst, - (long) data->param3); - *dst = (long) data->param3; - return -1; - } - - return 0; -} - - -static int wpa_config_parse_str(const struct global_parse_data *data, - struct wpa_config *config, int line, - const char *pos) -{ - size_t len; - char **dst, *tmp; - - len = os_strlen(pos); - if (data->param2 && len < (size_t) data->param2) { - wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " - "min_len=%ld)", line, data->name, - (unsigned long) len, (long) data->param2); - return -1; - } - - if (data->param3 && len > (size_t) data->param3) { - wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " - "max_len=%ld)", line, data->name, - (unsigned long) len, (long) data->param3); - return -1; - } - - tmp = os_strdup(pos); - if (tmp == NULL) - return -1; - - dst = (char **) (((u8 *) config) + (long) data->param1); - os_free(*dst); - *dst = tmp; - wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst); - - return 0; -} - - -static int wpa_config_process_country(const struct global_parse_data *data, - struct wpa_config *config, int line, - const char *pos) -{ - if (!pos[0] || !pos[1]) { - wpa_printf(MSG_DEBUG, "Invalid country set"); - return -1; - } - config->country[0] = pos[0]; - config->country[1] = pos[1]; - wpa_printf(MSG_DEBUG, "country='%c%c'", - config->country[0], config->country[1]); - return 0; -} - - -static int wpa_config_process_load_dynamic_eap( - const struct global_parse_data *data, struct wpa_config *config, - int line, const char *so) -{ - int ret; - wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); - ret = eap_peer_method_load(so); - if (ret == -2) { - wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " - "reloading."); - } else if (ret) { - wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " - "method '%s'.", line, so); - return -1; - } - - return 0; -} - - -#ifdef CONFIG_WPS - -static int wpa_config_process_uuid(const struct global_parse_data *data, - struct wpa_config *config, int line, - const char *pos) -{ - char buf[40]; - if (uuid_str2bin(pos, config->uuid)) { - wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); - return -1; - } - uuid_bin2str(config->uuid, buf, sizeof(buf)); - wpa_printf(MSG_DEBUG, "uuid=%s", buf); - return 0; -} - - -static int wpa_config_process_os_version(const struct global_parse_data *data, - struct wpa_config *config, int line, - const char *pos) -{ - if (hexstr2bin(pos, config->os_version, 4)) { - wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); - return -1; - } - wpa_printf(MSG_DEBUG, "os_version=%08x", - WPA_GET_BE32(config->os_version)); - return 0; -} - -#endif /* CONFIG_WPS */ - - -#ifdef OFFSET -#undef OFFSET -#endif /* OFFSET */ -/* OFFSET: Get offset of a variable within the wpa_config structure */ -#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) - -#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL -#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL -#define _INT(f) #f, wpa_config_parse_int, OFFSET(f) -#define INT(f) _INT(f), NULL, NULL -#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max -#define _STR(f) #f, wpa_config_parse_str, OFFSET(f) -#define STR(f) _STR(f), NULL, NULL -#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max - -static const struct global_parse_data global_fields[] = { -#ifdef CONFIG_CTRL_IFACE - { STR(ctrl_interface) }, - { STR(ctrl_interface_group) } /* deprecated */, -#endif /* CONFIG_CTRL_IFACE */ - { INT_RANGE(eapol_version, 1, 2) }, - { INT(ap_scan) }, - { INT(fast_reauth) }, - { STR(opensc_engine_path) }, - { STR(pkcs11_engine_path) }, - { STR(pkcs11_module_path) }, - { STR(driver_param) }, - { INT(dot11RSNAConfigPMKLifetime) }, - { INT(dot11RSNAConfigPMKReauthThreshold) }, - { INT(dot11RSNAConfigSATimeout) }, -#ifndef CONFIG_NO_CONFIG_WRITE - { INT(update_config) }, -#endif /* CONFIG_NO_CONFIG_WRITE */ - { FUNC_NO_VAR(load_dynamic_eap) }, -#ifdef CONFIG_WPS - { FUNC(uuid) }, - { STR_RANGE(device_name, 0, 32) }, - { STR_RANGE(manufacturer, 0, 64) }, - { STR_RANGE(model_name, 0, 32) }, - { STR_RANGE(model_number, 0, 32) }, - { STR_RANGE(serial_number, 0, 32) }, - { STR(device_type) }, - { FUNC(os_version) }, - { STR(config_methods) }, - { INT_RANGE(wps_cred_processing, 0, 2) }, -#endif /* CONFIG_WPS */ - { FUNC(country) }, - { INT(bss_max_count) }, - { INT_RANGE(filter_ssids, 0, 1) } -}; - -#undef FUNC -#undef _INT -#undef INT -#undef INT_RANGE -#undef _STR -#undef STR -#undef STR_RANGE -#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) - - -static int wpa_config_process_global(struct wpa_config *config, char *pos, - int line) -{ - size_t i; - int ret = 0; - - for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { - const struct global_parse_data *field = &global_fields[i]; - size_t flen = os_strlen(field->name); - if (os_strncmp(pos, field->name, flen) != 0 || - pos[flen] != '=') - continue; - - if (field->parser(field, config, line, pos + flen + 1)) { - wpa_printf(MSG_ERROR, "Line %d: failed to " - "parse '%s'.", line, pos); - ret = -1; - } - break; - } - if (i == NUM_GLOBAL_FIELDS) { - wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", - line, pos); - ret = -1; - } - - return ret; -} - - struct wpa_config * wpa_config_read(const char *name) { FILE *f; @@ -564,11 +332,13 @@ struct wpa_config * wpa_config_read(const char *name) config->ssid = head; wpa_config_debug_dump_networks(config); +#ifndef WPA_IGNORE_CONFIG_ERRORS if (errors) { wpa_config_free(config); config = NULL; head = NULL; } +#endif /* WPA_IGNORE_CONFIG_ERRORS */ return config; } @@ -726,6 +496,18 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) } +#ifdef CONFIG_P2P +static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "p2p_client_list"); + if (value == NULL) + return; + fprintf(f, "\tp2p_client_list=%s\n", value); + os_free(value); +} +#endif /* CONFIG_P2P */ + + static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) { int i; @@ -745,6 +527,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_pairwise(f, ssid); write_group(f, ssid); write_auth_alg(f, ssid); + STR(bgscan); #ifdef IEEE8021X_EAPOL write_eap(f, ssid); STR(identity); @@ -800,6 +583,9 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(ieee80211w); #endif /* CONFIG_IEEE80211W */ STR(id_str); +#ifdef CONFIG_P2P + write_p2p_client_list(f, ssid); +#endif /* CONFIG_P2P */ #undef STR #undef INT @@ -876,8 +662,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "model_number=%s\n", config->model_number); if (config->serial_number) fprintf(f, "serial_number=%s\n", config->serial_number); - if (config->device_type) - fprintf(f, "device_type=%s\n", config->device_type); + { + char _buf[WPS_DEV_TYPE_BUFSIZE], *buf; + buf = wps_dev_type_bin2str(config->device_type, + _buf, sizeof(_buf)); + if (os_strcmp(buf, "0-00000000-0") != 0) + fprintf(f, "device_type=%s\n", buf); + } if (WPA_GET_BE32(config->os_version)) fprintf(f, "os_version=%08x\n", WPA_GET_BE32(config->os_version)); @@ -887,14 +678,80 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "wps_cred_processing=%d\n", config->wps_cred_processing); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (config->p2p_listen_reg_class) + fprintf(f, "p2p_listen_reg_class=%u\n", + config->p2p_listen_reg_class); + if (config->p2p_listen_channel) + fprintf(f, "p2p_listen_channel=%u\n", + config->p2p_listen_channel); + if (config->p2p_oper_reg_class) + fprintf(f, "p2p_oper_reg_class=%u\n", + config->p2p_oper_reg_class); + if (config->p2p_oper_channel) + fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel); + if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT) + fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent); + if (config->p2p_ssid_postfix) + fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix); + if (config->persistent_reconnect) + fprintf(f, "persistent_reconnect=%u\n", + config->persistent_reconnect); + if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS) + fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss); + if (config->p2p_group_idle) + fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle); + if (config->p2p_pref_chan) { + unsigned int i; + fprintf(f, "p2p_pref_chan="); + for (i = 0; i < config->num_p2p_pref_chan; i++) { + fprintf(f, "%s%u:%u", i > 0 ? "," : "", + config->p2p_pref_chan[i].op_class, + config->p2p_pref_chan[i].chan); + } + fprintf(f, "\n"); + } +#endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", config->country[0], config->country[1]); } if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT) fprintf(f, "bss_max_count=%u\n", config->bss_max_count); + if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE) + fprintf(f, "bss_expiration_age=%u\n", + config->bss_expiration_age); + if (config->bss_expiration_scan_count != + DEFAULT_BSS_EXPIRATION_SCAN_COUNT) + fprintf(f, "bss_expiration_scan_count=%u\n", + config->bss_expiration_scan_count); if (config->filter_ssids) fprintf(f, "filter_ssids=%d\n", config->filter_ssids); + if (config->max_num_sta != DEFAULT_MAX_NUM_STA) + fprintf(f, "max_num_sta=%u\n", config->max_num_sta); + if (config->disassoc_low_ack) + fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack); +#ifdef CONFIG_INTERWORKING + if (config->home_realm) + fprintf(f, "home_realm=%s\n", config->home_realm); + if (config->home_username) + fprintf(f, "home_username=%s\n", config->home_username); + if (config->home_password) + fprintf(f, "home_password=%s\n", config->home_password); + if (config->home_ca_cert) + fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert); + if (config->home_imsi) + fprintf(f, "home_imsi=%s\n", config->home_imsi); + if (config->home_milenage) + fprintf(f, "home_milenage=%s\n", config->home_milenage); + if (config->interworking) + fprintf(f, "interworking=%u\n", config->interworking); + if (!is_zero_ether_addr(config->hessid)) + fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid)); + if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE) + fprintf(f, "access_network_type=%d\n", + config->access_network_type); +#endif /* CONFIG_INTERWORKING */ } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -921,8 +778,11 @@ int wpa_config_write(const char *name, struct wpa_config *config) wpa_config_write_global(f, config); for (ssid = config->ssid; ssid; ssid = ssid->next) { - if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) - continue; /* do not save temporary WPS networks */ + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary) + continue; /* do not save temporary networks */ + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && + !ssid->passphrase) + continue; /* do not save invalid network */ fprintf(f, "\nnetwork={\n"); wpa_config_write_network(f, ssid); fprintf(f, "}\n"); diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 25e87aa..8a47c0b 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -109,6 +109,9 @@ struct wpa_ssid { * * If set, this network block is used only when associating with the AP * using the configured BSSID + * + * If this is a persistent P2P group (disabled == 2), this is the GO + * Device Address. */ u8 bssid[ETH_ALEN]; @@ -273,6 +276,11 @@ struct wpa_ssid { * * 2 = AP (access point) * + * 3 = P2P Group Owner (can be set in the configuration file) + * + * 4 = P2P Group Formation (used internally; not in configuration + * files) + * * Note: IBSS can only be used with key_mgmt NONE (plaintext and * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires @@ -284,6 +292,8 @@ struct wpa_ssid { WPAS_MODE_INFRA = 0, WPAS_MODE_IBSS = 1, WPAS_MODE_AP = 2, + WPAS_MODE_P2P_GO = 3, + WPAS_MODE_P2P_GROUP_FORMATION = 4, } mode; /** @@ -292,6 +302,8 @@ struct wpa_ssid { * 0 = this network can be used (default). * 1 = this network block is disabled (can be enabled through * ctrl_iface, e.g., with wpa_cli or wpa_gui). + * 2 = this network block includes parameters for a persistent P2P + * group (can be used with P2P ctrl_iface commands) */ int disabled; @@ -373,6 +385,43 @@ struct wpa_ssid { * considered when selecting a BSS. */ int *freq_list; + + /** + * p2p_client_list - List of P2P Clients in a persistent group (GO) + * + * This is a list of P2P Clients (P2P Device Address) that have joined + * the persistent group. This is maintained on the GO for persistent + * group entries (disabled == 2). + */ + u8 *p2p_client_list; + + /** + * num_p2p_clients - Number of entries in p2p_client_list + */ + size_t num_p2p_clients; + + /** + * p2p_group - Network generated as a P2P group (used internally) + */ + int p2p_group; + + /** + * p2p_persistent_group - Whether this is a persistent group + */ + int p2p_persistent_group; + + /** + * temporary - Whether this network is temporary and not to be saved + */ + int temporary; + + /** + * export_keys - Whether keys may be exported + * + * This attribute will be set when keys are determined through + * WPS or similar so that they may be exported. + */ + int export_keys; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 9a7825a..ea3a2ac 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -247,8 +247,13 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) hk, TEXT("model_name")); config->serial_number = wpa_config_read_reg_string( hk, TEXT("serial_number")); - config->device_type = wpa_config_read_reg_string( - hk, TEXT("device_type")); + { + char *t = wpa_config_read_reg_string( + hk, TEXT("device_type")); + if (t && wps_dev_type_str2bin(t, config->device_type)) + errors++; + os_free(t); + } config->config_methods = wpa_config_read_reg_string( hk, TEXT("config_methods")); if (wpa_config_read_global_os_version(config, hk)) @@ -256,11 +261,21 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), &config->wps_cred_processing); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + config->p2p_ssid_postfix = wpa_config_read_reg_string( + hk, TEXT("p2p_ssid_postfix")); + wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"), + (int *) &config->p2p_group_idle); +#endif /* CONFIG_P2P */ wpa_config_read_reg_dword(hk, TEXT("bss_max_count"), (int *) &config->bss_max_count); wpa_config_read_reg_dword(hk, TEXT("filter_ssids"), &config->filter_ssids); + wpa_config_read_reg_dword(hk, TEXT("max_num_sta"), + (int *) &config->max_num_sta); + wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"), + (int *) &config->disassoc_low_ack); return errors ? -1 : 0; } @@ -575,7 +590,12 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) wpa_config_write_reg_string(hk, "model_number", config->model_number); wpa_config_write_reg_string(hk, "serial_number", config->serial_number); - wpa_config_write_reg_string(hk, "device_type", config->device_type); + { + char _buf[WPS_DEV_TYPE_BUFSIZE], *buf; + buf = wps_dev_type_bin2str(config->device_type, + _buf, sizeof(_buf)); + wpa_config_write_reg_string(hk, "device_type", buf); + } wpa_config_write_reg_string(hk, "config_methods", config->config_methods); if (WPA_GET_BE32(config->os_version)) { @@ -587,12 +607,22 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), config->wps_cred_processing, 0); #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + wpa_config_write_reg_string(hk, "p2p_ssid_postfix", + config->p2p_ssid_postfix); + wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"), + config->p2p_group_idle, 0); +#endif /* CONFIG_P2P */ wpa_config_write_reg_dword(hk, TEXT("bss_max_count"), config->bss_max_count, DEFAULT_BSS_MAX_COUNT); wpa_config_write_reg_dword(hk, TEXT("filter_ssids"), config->filter_ssids, 0); + wpa_config_write_reg_dword(hk, TEXT("max_num_sta"), + config->max_num_sta, DEFAULT_MAX_NUM_STA); + wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"), + config->disassoc_low_ack, 0); return 0; } diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 19fea29..5ff6766 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -16,6 +16,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" @@ -31,10 +32,15 @@ #include "wps_supplicant.h" #include "ibss_rsn.h" #include "ap.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "ctrl_iface.h" +#include "interworking.h" +#include "blacklist.h" +#include "wpas_glue.h" extern struct wpa_driver_ops *wpa_drivers[]; @@ -80,13 +86,77 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) ret = -1; - } else - ret = -1; + } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { + wpa_s->wps_fragment_size = atoi(value); +#ifdef CONFIG_WPS_TESTING + } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { + long int val; + val = strtol(value, NULL, 0); + if (val < 0 || val > 0xff) { + ret = -1; + wpa_printf(MSG_DEBUG, "WPS: Invalid " + "wps_version_number %ld", val); + } else { + wps_version_number = val; + wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " + "version %u.%u", + (wps_version_number & 0xf0) >> 4, + wps_version_number & 0x0f); + } + } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { + wps_testing_dummy_cred = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", + wps_testing_dummy_cred); +#endif /* CONFIG_WPS_TESTING */ + } else if (os_strcasecmp(cmd, "ampdu") == 0) { + if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) + ret = -1; +#ifdef CONFIG_TDLS_TESTING + } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { + extern unsigned int tdls_testing; + tdls_testing = strtol(value, NULL, 0); + wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); +#endif /* CONFIG_TDLS_TESTING */ +#ifdef CONFIG_TDLS + } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) { + int disabled = atoi(value); + wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled); + if (disabled) { + if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0) + ret = -1; + } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0) + ret = -1; + wpa_tdls_enable(wpa_s->wpa, !disabled); +#endif /* CONFIG_TDLS */ + } else { + value[-1] = '='; + ret = wpa_config_process_global(wpa_s->conf, cmd, -1); + if (ret == 0) + wpa_supplicant_update_config(wpa_s); + } return ret; } +static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, size_t buflen) +{ + int res; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); + + if (os_strcmp(cmd, "version") == 0) { + res = os_snprintf(buf, buflen, "%s", VERSION_STR); + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; + } + + return -1; +} + + #ifdef IEEE8021X_EAPOL static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, char *addr) @@ -131,6 +201,80 @@ static int wpa_supplicant_ctrl_iface_stkstart( #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + +static int wpa_supplicant_ctrl_iface_tdls_discover( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + int ret; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR, + MAC2STR(peer)); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer); + + return ret; +} + + +static int wpa_supplicant_ctrl_iface_tdls_setup( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + int ret; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR, + MAC2STR(peer)); + + ret = wpa_tdls_reneg(wpa_s->wpa, peer); + if (ret) { + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_start(wpa_s->wpa, peer); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); + } + + return ret; +} + + +static int wpa_supplicant_ctrl_iface_tdls_teardown( + struct wpa_supplicant *wpa_s, char *addr) +{ + u8 peer[ETH_ALEN]; + + if (hwaddr_aton(addr, peer)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " + "address '%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR, + MAC2STR(peer)); + + return wpa_tdls_teardown_link(wpa_s->wpa, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); +} + +#endif /* CONFIG_TDLS */ + + #ifdef CONFIG_IEEE80211R static int wpa_supplicant_ctrl_iface_ft_ds( struct wpa_supplicant *wpa_s, char *addr) @@ -163,10 +307,26 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, char *cmd) { u8 bssid[ETH_ALEN], *_bssid = bssid; +#ifdef CONFIG_P2P + u8 p2p_dev_addr[ETH_ALEN]; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_AP + u8 *_p2p_dev_addr = NULL; +#endif /* CONFIG_AP */ - if (cmd == NULL || os_strcmp(cmd, "any") == 0) + if (cmd == NULL || os_strcmp(cmd, "any") == 0) { _bssid = NULL; - else if (hwaddr_aton(cmd, bssid)) { +#ifdef CONFIG_P2P + } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { + if (hwaddr_aton(cmd + 13, p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid " + "P2P Device Address '%s'", + cmd + 13); + return -1; + } + _p2p_dev_addr = p2p_dev_addr; +#endif /* CONFIG_P2P */ + } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", cmd); return -1; @@ -174,10 +334,10 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, #ifdef CONFIG_AP if (wpa_s->ap_iface) - return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid); + return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); #endif /* CONFIG_AP */ - return wpas_wps_start_pbc(wpa_s, _bssid); + return wpas_wps_start_pbc(wpa_s, _bssid, 0); } @@ -195,7 +355,10 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, if (os_strcmp(cmd, "any") == 0) _bssid = NULL; - else if (hwaddr_aton(cmd, bssid)) { + else if (os_strcmp(cmd, "get") == 0) { + ret = wps_generate_pin(); + goto done; + } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", cmd); return -1; @@ -208,7 +371,8 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, #endif /* CONFIG_AP */ if (pin) { - ret = wpas_wps_start_pin(wpa_s, _bssid, pin); + ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, + DEV_PW_DEFAULT); if (ret < 0) return -1; ret = os_snprintf(buf, buflen, "%s", pin); @@ -217,10 +381,11 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, return ret; } - ret = wpas_wps_start_pin(wpa_s, _bssid, NULL); + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT); if (ret < 0) return -1; +done: /* Return the generated PIN */ ret = os_snprintf(buf, buflen, "%08d", ret); if (ret < 0 || (size_t) ret >= buflen) @@ -229,6 +394,51 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, } +static int wpa_supplicant_ctrl_iface_wps_check_pin( + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) +{ + char pin[9]; + size_t len; + char *pos; + int ret; + + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", + (u8 *) cmd, os_strlen(cmd)); + for (pos = cmd, len = 0; *pos != '\0'; pos++) { + if (*pos < '0' || *pos > '9') + continue; + pin[len++] = *pos; + if (len == 9) { + wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); + return -1; + } + } + if (len != 4 && len != 8) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); + return -1; + } + pin[len] = '\0'; + + if (len == 8) { + unsigned int pin_val; + pin_val = atoi(pin); + if (!wps_pin_valid(pin_val)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); + ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + } + + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + + return ret; +} + + #ifdef CONFIG_WPS_OOB static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s, char *cmd) @@ -257,7 +467,7 @@ static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s, static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, char *cmd) { - u8 bssid[ETH_ALEN], *_bssid = bssid; + u8 bssid[ETH_ALEN]; char *pin; char *new_ssid; char *new_auth; @@ -270,9 +480,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, return -1; *pin++ = '\0'; - if (os_strcmp(cmd, "any") == 0) - _bssid = NULL; - else if (hwaddr_aton(cmd, bssid)) { + if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", cmd); return -1; @@ -280,7 +488,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, new_ssid = os_strchr(pin, ' '); if (new_ssid == NULL) - return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL); + return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); *new_ssid++ = '\0'; new_auth = os_strchr(new_ssid, ' '); @@ -303,20 +511,86 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, ap.auth = new_auth; ap.encr = new_encr; ap.key_hex = new_key; - return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap); + return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); +} + + +#ifdef CONFIG_AP +static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + if (!wpa_s->ap_iface) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(cmd, "disable") == 0) { + wpas_wps_ap_pin_disable(wpa_s); + return os_snprintf(buf, buflen, "OK\n"); + } + + if (os_strcmp(cmd, "random") == 0) { + if (pos) + timeout = atoi(pos); + pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "get") == 0) { + pin_txt = wpas_wps_ap_pin_get(wpa_s); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "set") == 0) { + char *pin; + if (pos == NULL) + return -1; + pin = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + if (os_strlen(pin) > buflen) + return -1; + if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0) + return -1; + return os_snprintf(buf, buflen, "%s", pin); + } + + return -1; } +#endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, char *cmd) { - char *uuid = cmd, *pin; + char *uuid = cmd, *pin, *pos; + u8 addr_buf[ETH_ALEN], *addr = NULL; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; - return wpas_wps_er_add_pin(wpa_s, uuid, pin); + pos = os_strchr(pin, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } + return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin); } @@ -330,6 +604,62 @@ static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s, *pin++ = '\0'; return wpas_wps_er_learn(wpa_s, uuid, pin); } + + +static int wpa_supplicant_ctrl_iface_wps_er_set_config( + struct wpa_supplicant *wpa_s, char *cmd) +{ + char *uuid = cmd, *id; + id = os_strchr(uuid, ' '); + if (id == NULL) + return -1; + *id++ = '\0'; + return wpas_wps_er_set_config(wpa_s, uuid, atoi(id)); +} + + +static int wpa_supplicant_ctrl_iface_wps_er_config( + struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pin; + char *new_ssid; + char *new_auth; + char *new_encr; + char *new_key; + struct wps_new_ap_settings ap; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + new_ssid = os_strchr(pin, ' '); + if (new_ssid == NULL) + return -1; + *new_ssid++ = '\0'; + + new_auth = os_strchr(new_ssid, ' '); + if (new_auth == NULL) + return -1; + *new_auth++ = '\0'; + + new_encr = os_strchr(new_auth, ' '); + if (new_encr == NULL) + return -1; + *new_encr++ = '\0'; + + new_key = os_strchr(new_encr, ' '); + if (new_key == NULL) + return -1; + *new_key++ = '\0'; + + os_memset(&ap, 0, sizeof(ap)); + ap.ssid_hex = new_ssid; + ap.auth = new_auth; + ap.encr = new_encr; + ap.key_hex = new_key; + return wpas_wps_er_config(wpa_s, cmd, pin, &ap); +} #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ @@ -355,84 +685,110 @@ static int wpa_supplicant_ctrl_iface_ibss_rsn( #endif /* CONFIG_IBSS_RSN */ -static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, - char *rsp) +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const char *field, + const char *value) { #ifdef IEEE8021X_EAPOL - char *pos, *id_pos; - int id; - struct wpa_ssid *ssid; - struct eap_peer_config *eap; - - pos = os_strchr(rsp, '-'); - if (pos == NULL) - return -1; - *pos++ = '\0'; - id_pos = pos; - pos = os_strchr(pos, ':'); - if (pos == NULL) - return -1; - *pos++ = '\0'; - id = atoi(id_pos); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); - wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", - (u8 *) pos, os_strlen(pos)); + struct eap_peer_config *eap = &ssid->eap; - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " - "to update", id); - return -1; - } - eap = &ssid->eap; + wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value", + (const u8 *) value, os_strlen(value)); - if (os_strcmp(rsp, "IDENTITY") == 0) { + switch (wpa_supplicant_ctrl_req_from_string(field)) { + case WPA_CTRL_REQ_EAP_IDENTITY: os_free(eap->identity); - eap->identity = (u8 *) os_strdup(pos); - eap->identity_len = os_strlen(pos); + eap->identity = (u8 *) os_strdup(value); + eap->identity_len = os_strlen(value); eap->pending_req_identity = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "PASSWORD") == 0) { + break; + case WPA_CTRL_REQ_EAP_PASSWORD: os_free(eap->password); - eap->password = (u8 *) os_strdup(pos); - eap->password_len = os_strlen(pos); + eap->password = (u8 *) os_strdup(value); + eap->password_len = os_strlen(value); eap->pending_req_password = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) { + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: os_free(eap->new_password); - eap->new_password = (u8 *) os_strdup(pos); - eap->new_password_len = os_strlen(pos); + eap->new_password = (u8 *) os_strdup(value); + eap->new_password_len = os_strlen(value); eap->pending_req_new_password = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "PIN") == 0) { + break; + case WPA_CTRL_REQ_EAP_PIN: os_free(eap->pin); - eap->pin = os_strdup(pos); + eap->pin = os_strdup(value); eap->pending_req_pin = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "OTP") == 0) { + break; + case WPA_CTRL_REQ_EAP_OTP: os_free(eap->otp); - eap->otp = (u8 *) os_strdup(pos); - eap->otp_len = os_strlen(pos); + eap->otp = (u8 *) os_strdup(value); + eap->otp_len = os_strlen(value); os_free(eap->pending_req_otp); eap->pending_req_otp = NULL; eap->pending_req_otp_len = 0; - } else if (os_strcmp(rsp, "PASSPHRASE") == 0) { + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: os_free(eap->private_key_passwd); - eap->private_key_passwd = (u8 *) os_strdup(pos); + eap->private_key_passwd = (u8 *) os_strdup(value); eap->pending_req_passphrase = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; - } else { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); + break; + default: + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; } return 0; #else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included"); + return -1; +#endif /* IEEE8021X_EAPOL */ +} + + +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, + char *rsp) +{ +#ifdef IEEE8021X_EAPOL + char *pos, *id_pos; + int id; + struct wpa_ssid *ssid; + + pos = os_strchr(rsp, '-'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id_pos = pos; + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(id_pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) pos, os_strlen(pos)); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + + return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp, + pos); +#else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); return -1; #endif /* IEEE8021X_EAPOL */ @@ -497,6 +853,15 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "mode=AP\n"); break; + case WPAS_MODE_P2P_GO: + ret = os_snprintf(pos, end - pos, + "mode=P2P GO\n"); + break; + case WPAS_MODE_P2P_GROUP_FORMATION: + ret = os_snprintf(pos, end - pos, + "mode=P2P GO - group " + "formation\n"); + break; default: ret = 0; break; @@ -529,14 +894,30 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += ret; } - if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || - wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, - verbose); - if (res >= 0) - pos += res; - } - +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR + "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_P2P */ + + ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", + MAC2STR(wpa_s->own_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, + verbose); + if (res >= 0) + pos += res; + } + res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); if (res >= 0) pos += res; @@ -579,6 +960,152 @@ static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s, } +static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN]; + struct wpa_blacklist *e; + char *pos, *end; + int ret; + + /* cmd: "BLACKLIST []" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + e = wpa_s->blacklist; + while (e) { + ret = os_snprintf(pos, end - pos, MACSTR "\n", + MAC2STR(e->bssid)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + e = e->next; + } + return pos - buf; + } + + cmd++; + if (os_strncmp(cmd, "clear", 5) == 0) { + wpa_blacklist_clear(wpa_s); + os_memcpy(buf, "OK\n", 3); + return 3; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd); + if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd); + return -1; + } + + /* + * Add the BSSID twice, so its count will be 2, causing it to be + * skipped when processing scan results. + */ + ret = wpa_blacklist_add(wpa_s, bssid); + if (ret != 0) + return -1; + ret = wpa_blacklist_add(wpa_s, bssid); + if (ret != 0) + return -1; + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +extern int wpa_debug_level; +extern int wpa_debug_timestamp; + +static const char * debug_level_str(int level) +{ + switch (level) { + case MSG_EXCESSIVE: + return "EXCESSIVE"; + case MSG_MSGDUMP: + return "MSGDUMP"; + case MSG_DEBUG: + return "DEBUG"; + case MSG_INFO: + return "INFO"; + case MSG_WARNING: + return "WARNING"; + case MSG_ERROR: + return "ERROR"; + default: + return "?"; + } +} + + +static int str_to_debug_level(const char *s) +{ + if (os_strcasecmp(s, "EXCESSIVE") == 0) + return MSG_EXCESSIVE; + if (os_strcasecmp(s, "MSGDUMP") == 0) + return MSG_MSGDUMP; + if (os_strcasecmp(s, "DEBUG") == 0) + return MSG_DEBUG; + if (os_strcasecmp(s, "INFO") == 0) + return MSG_INFO; + if (os_strcasecmp(s, "WARNING") == 0) + return MSG_WARNING; + if (os_strcasecmp(s, "ERROR") == 0) + return MSG_ERROR; + return -1; +} + + +static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + char *pos, *end, *stamp; + int ret; + + if (cmd == NULL) { + return -1; + } + + /* cmd: "LOG_LEVEL []" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "Current level: %s\n" + "Timestamp: %d\n", + debug_level_str(wpa_debug_level), + wpa_debug_timestamp); + if (ret < 0 || ret >= end - pos) + ret = 0; + + return ret; + } + + while (*cmd == ' ') + cmd++; + + stamp = os_strchr(cmd, ' '); + if (stamp) { + *stamp++ = '\0'; + while (*stamp == ' ') { + stamp++; + } + } + + if (cmd && os_strlen(cmd)) { + int level = str_to_debug_level(cmd); + if (level < 0) + return -1; + wpa_debug_level = level; + } + + if (stamp && os_strlen(stamp)) + wpa_debug_timestamp = atoi(stamp); + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + static int wpa_supplicant_ctrl_iface_list_networks( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -611,10 +1138,12 @@ static int wpa_supplicant_ctrl_iface_list_networks( if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - ret = os_snprintf(pos, end - pos, "\t%s%s", + ret = os_snprintf(pos, end - pos, "\t%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", - ssid->disabled ? "[DISABLED]" : ""); + ssid->disabled ? "[DISABLED]" : "", + ssid->disabled == 2 ? "[P2P-PERSISTENT]" : + ""); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; @@ -774,7 +1303,8 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, #ifdef CONFIG_WPS -static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, +static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, + char *pos, char *end, struct wpabuf *wps_ie) { int ret; @@ -784,6 +1314,10 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, return pos; if (wps_is_selected_pbc_registrar(wps_ie)) txt = "[WPS-PBC]"; +#ifdef CONFIG_WPS2 + else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0)) + txt = "[WPS-AUTH]"; +#endif /* CONFIG_WPS2 */ else if (wps_is_selected_pin_registrar(wps_ie)) txt = "[WPS-PIN]"; else @@ -798,13 +1332,14 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, #endif /* CONFIG_WPS */ -static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, +static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s, + char *pos, char *end, const struct wpa_bss *bss) { #ifdef CONFIG_WPS struct wpabuf *wps_ie; wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); - return wpa_supplicant_wps_ie_txt_buf(pos, end, wps_ie); + return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie); #else /* CONFIG_WPS */ return pos; #endif /* CONFIG_WPS */ @@ -813,11 +1348,18 @@ static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, /* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( + struct wpa_supplicant *wpa_s, const struct wpa_bss *bss, char *buf, size_t buflen) { char *pos, *end; int ret; - const u8 *ie, *ie2; + const u8 *ie, *ie2, *p2p; + + p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); + if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == + 0) + return 0; /* Do not show P2P listen discovery results here */ pos = buf; end = buf + buflen; @@ -825,7 +1367,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", MAC2STR(bss->bssid), bss->freq, bss->level); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) @@ -833,35 +1375,41 @@ static int wpa_supplicant_ctrl_iface_scan_result( ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(pos, end, bss); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; pos += ret; } if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; + pos += ret; + } + if (p2p) { + ret = os_snprintf(pos, end - pos, "[P2P]"); + if (ret < 0 || ret >= end - pos) + return -1; pos += ret; } ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) - return pos - buf; + return -1; pos += ret; return pos - buf; @@ -884,7 +1432,7 @@ static int wpa_supplicant_ctrl_iface_scan_results( pos += ret; dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { - ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos, + ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos, end - pos); if (ret < 0 || ret >= end - pos) return pos - buf; @@ -915,6 +1463,11 @@ static int wpa_supplicant_ctrl_iface_select_network( "network id=%d", id); return -1; } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "SELECT_NETWORK with persistent P2P group"); + return -1; + } } wpa_supplicant_select_network(wpa_s, ssid); @@ -943,6 +1496,11 @@ static int wpa_supplicant_ctrl_iface_enable_network( "network id=%d", id); return -1; } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "ENABLE_NETWORK with persistent P2P group"); + return -1; + } } wpa_supplicant_enable_network(wpa_s, ssid); @@ -970,6 +1528,12 @@ static int wpa_supplicant_ctrl_iface_disable_network( "network id=%d", id); return -1; } + if (ssid->disabled == 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use " + "DISABLE_NETWORK with persistent P2P " + "group"); + return -1; + } } wpa_supplicant_disable_network(wpa_s, ssid); @@ -1018,8 +1582,13 @@ static int wpa_supplicant_ctrl_iface_remove_network( wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } + eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { - eapol_sm_invalidate_cached_session(wpa_s->eapol); +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -1030,6 +1599,8 @@ static int wpa_supplicant_ctrl_iface_remove_network( wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid) + wpas_notify_network_removed(wpa_s, ssid); if (ssid == NULL || wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " @@ -1037,12 +1608,20 @@ static int wpa_supplicant_ctrl_iface_remove_network( return -1; } - if (ssid == wpa_s->current_ssid) { + if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ /* - * Invalidate the EAP session cache if the current network is - * removed. + * Invalidate the EAP session cache if the current or + * previously used network is removed. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + + if (ssid == wpa_s->current_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -1088,10 +1667,12 @@ static int wpa_supplicant_ctrl_iface_set_network( return -1; } - if (wpa_s->current_ssid == ssid) { + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + + if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { /* * Invalidate the EAP session cache if anything in the current - * configuration changes. + * or previously used configuration changes. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } @@ -1482,6 +2063,41 @@ static int wpa_supplicant_ctrl_iface_get_capability( } +#ifdef CONFIG_INTERWORKING +static char * anqp_add_hex(char *pos, char *end, const char *title, + struct wpabuf *data) +{ + char *start = pos; + size_t i; + int ret; + const u8 *d; + + if (data == NULL) + return start; + + ret = os_snprintf(pos, end - pos, "%s=", title); + if (ret < 0 || ret >= end - pos) + return start; + pos += ret; + + d = wpabuf_head_u8(data); + for (i = 0; i < wpabuf_len(data); i++) { + ret = os_snprintf(pos, end - pos, "%02x", *d++); + if (ret < 0 || ret >= end - pos) + return start; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return start; + pos += ret; + + return pos; +} +#endif /* CONFIG_INTERWORKING */ + + static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) @@ -1509,6 +2125,13 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, bss = dl_list_entry(next, struct wpa_bss, list_id); } +#ifdef CONFIG_P2P + } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { + if (hwaddr_aton(cmd + 13, bssid) == 0) + bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); + else + bss = NULL; +#endif /* CONFIG_P2P */ } else if (hwaddr_aton(cmd, bssid) == 0) bss = wpa_bss_get_bssid(wpa_s, bssid); else { @@ -1572,7 +2195,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(pos, end, bss); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) @@ -1591,6 +2214,12 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, return pos - buf; pos += ret; } + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) { + ret = os_snprintf(pos, end - pos, "[P2P]"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) @@ -1611,6 +2240,28 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, pos += ret; #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + ie = (const u8 *) (bss + 1); + ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + pos = anqp_add_hex(pos, end, "anqp_venue_name", bss->anqp_venue_name); + pos = anqp_add_hex(pos, end, "anqp_network_auth_type", + bss->anqp_network_auth_type); + pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", + bss->anqp_roaming_consortium); + pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", + bss->anqp_ip_addr_type_availability); + pos = anqp_add_hex(pos, end, "anqp_nai_realm", bss->anqp_nai_realm); + pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp); + pos = anqp_add_hex(pos, end, "anqp_domain_name", + bss->anqp_domain_name); +#endif /* CONFIG_INTERWORKING */ + return pos - buf; } @@ -1623,19 +2274,44 @@ static int wpa_supplicant_ctrl_iface_ap_scan( } -static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) +static int wpa_supplicant_ctrl_iface_scan_interval( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int scan_int = atoi(cmd); + if (scan_int < 0) + return -1; + wpa_s->scan_interval = scan_int; + return 0; +} + + +static int wpa_supplicant_ctrl_iface_bss_expire_age( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int expire_age = atoi(cmd); + return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age); +} + + +static int wpa_supplicant_ctrl_iface_bss_expire_count( + struct wpa_supplicant *wpa_s, char *cmd) { - u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; + int expire_count = atoi(cmd); + return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count); +} + +static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) +{ wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); /* MLME-DELETEKEYS.request */ - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0); #ifdef CONFIG_IEEE80211W - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0); #endif /* CONFIG_IEEE80211W */ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL, @@ -1651,6 +2327,9 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, char *addr) { +#ifdef CONFIG_NO_SCAN_PROCESSING + return -1; +#else /* CONFIG_NO_SCAN_PROCESSING */ u8 bssid[ETH_ALEN]; struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; @@ -1685,68 +2364,995 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, wpa_supplicant_connect(wpa_s, bss, ssid); return 0; +#endif /* CONFIG_NO_SCAN_PROCESSING */ } -char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, - char *buf, size_t *resp_len) +#ifdef CONFIG_P2P +static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) { - char *reply; - const int reply_size = 2048; - int ctrl_rsp = 0; - int reply_len; + unsigned int timeout = atoi(cmd); + enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; + u8 dev_id[ETH_ALEN], *_dev_id = NULL; + char *pos; - if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || - os_strncmp(buf, "SET_NETWORK ", 12) == 0) { - wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", - (const u8 *) buf, os_strlen(buf)); + if (os_strstr(cmd, "type=social")) + type = P2P_FIND_ONLY_SOCIAL; + else if (os_strstr(cmd, "type=progressive")) + type = P2P_FIND_PROGRESSIVE; + + pos = os_strstr(cmd, "dev_id="); + if (pos) { + pos += 7; + if (hwaddr_aton(pos, dev_id)) + return -1; + _dev_id = dev_id; + } + + return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id); +} + + +static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + char *pos, *pos2; + char *pin = NULL; + enum p2p_wps_method wps_method; + int new_pin; + int ret; + int persistent_group; + int join; + int auth; + int go_intent = -1; + int freq = 0; + + /* <"pbc" | "pin" | PIN> [label|display|keypad] [persistent] + * [join] [auth] [go_intent=<0..15>] [freq=] */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + persistent_group = os_strstr(pos, " persistent") != NULL; + join = os_strstr(pos, " join") != NULL; + auth = os_strstr(pos, " auth") != NULL; + + pos2 = os_strstr(pos, " go_intent="); + if (pos2) { + pos2 += 11; + go_intent = atoi(pos2); + if (go_intent < 0 || go_intent > 15) + return -1; + } + + pos2 = os_strstr(pos, " freq="); + if (pos2) { + pos2 += 6; + freq = atoi(pos2); + if (freq <= 0) + return -1; + } + + if (os_strncmp(pos, "pin", 3) == 0) { + /* Request random PIN (to be displayed) and enable the PIN */ + wps_method = WPS_PIN_DISPLAY; + } else if (os_strncmp(pos, "pbc", 3) == 0) { + wps_method = WPS_PBC; } else { - wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", - (const u8 *) buf, os_strlen(buf)); + pin = pos; + pos = os_strchr(pin, ' '); + wps_method = WPS_PIN_KEYPAD; + if (pos) { + *pos++ = '\0'; + if (os_strncmp(pos, "display", 7) == 0) + wps_method = WPS_PIN_DISPLAY; + } } - reply = os_malloc(reply_size); - if (reply == NULL) { - *resp_len = 1; - return NULL; + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, + persistent_group, join, auth, go_intent, + freq); + if (new_pin == -2) { + os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); + return 25; + } + if (new_pin == -3) { + os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25); + return 25; + } + if (new_pin < 0) + return -1; + if (wps_method == WPS_PIN_DISPLAY && pin == NULL) { + ret = os_snprintf(buf, buflen, "%08d", new_pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; } - os_memcpy(reply, "OK\n", 3); - reply_len = 3; + os_memcpy(buf, "OK\n", 3); + return 3; +} - if (os_strcmp(buf, "PING") == 0) { - os_memcpy(reply, "PONG\n", 5); - reply_len = 5; - } else if (os_strcmp(buf, "MIB") == 0) { - reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); - if (reply_len >= 0) { - int res; - res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, - reply_size - reply_len); - if (res < 0) - reply_len = -1; - else - reply_len += res; + +static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) +{ + unsigned int timeout = atoi(cmd); + return wpas_p2p_listen(wpa_s, timeout); +} + + +static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *pos; + + /* [join] */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + return wpas_p2p_prov_disc(wpa_s, addr, pos, + os_strstr(pos, "join") != NULL); +} + + +static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + ssid->passphrase == NULL) + return -1; + + os_strlcpy(buf, ssid->passphrase, buflen); + return os_strlen(buf); +} + + +static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u64 ref; + int res; + u8 dst_buf[ETH_ALEN], *dst; + struct wpabuf *tlvs; + char *pos; + size_t len; + + if (hwaddr_aton(cmd, dst_buf)) + return -1; + dst = dst_buf; + if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && + dst[3] == 0 && dst[4] == 0 && dst[5] == 0) + dst = NULL; + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + if (os_strncmp(pos, "upnp ", 5) == 0) { + u8 version; + pos += 5; + if (hexstr2bin(pos, &version, 1) < 0) + return -1; + pos += 2; + if (*pos != ' ') + return -1; + pos++; + ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, dst, + version, pos); + } else { + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + tlvs = wpabuf_alloc(len); + if (tlvs == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) { + wpabuf_free(tlvs); + return -1; } - } else if (os_strncmp(buf, "STATUS", 6) == 0) { - reply_len = wpa_supplicant_ctrl_iface_status( - wpa_s, buf + 6, reply, reply_size); + + ref = (unsigned long) wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + } + res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); + if (res < 0 || (unsigned) res >= buflen) + return -1; + return res; +} + + +static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s, + char *cmd) +{ + long long unsigned val; + u64 req; + if (sscanf(cmd, "%llx", &val) != 1) + return -1; + req = val; + return wpas_p2p_sd_cancel_request(wpa_s, (void *) (unsigned long) req); +} + + +static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq; + u8 dst[ETH_ALEN]; + u8 dialog_token; + struct wpabuf *resp_tlvs; + char *pos, *pos2; + size_t len; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + freq = atoi(cmd); + if (freq == 0) + return -1; + + if (hwaddr_aton(pos, dst)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + return -1; + *pos2++ = '\0'; + dialog_token = atoi(pos); + + len = os_strlen(pos2); + if (len & 1) + return -1; + len /= 2; + resp_tlvs = wpabuf_alloc(len); + if (resp_tlvs == NULL) + return -1; + if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) { + wpabuf_free(resp_tlvs); + return -1; + } + + wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs); + wpabuf_free(resp_tlvs); + return 0; +} + + +static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s, + char *cmd) +{ + if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1")) + return -1; + wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd); + return 0; +} + + +static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos; + size_t len; + struct wpabuf *query, *resp; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + query = wpabuf_alloc(len); + if (query == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { + wpabuf_free(query); + return -1; + } + + len = os_strlen(pos); + if (len & 1) { + wpabuf_free(query); + return -1; + } + len /= 2; + resp = wpabuf_alloc(len); + if (resp == NULL) { + wpabuf_free(query); + return -1; + } + if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) { + wpabuf_free(query); + wpabuf_free(resp); + return -1; + } + + if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { + wpabuf_free(query); + wpabuf_free(resp); + return -1; + } + return 0; +} + + +static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 version; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (hexstr2bin(cmd, &version, 1) < 0) + return -1; + + return wpas_p2p_service_add_upnp(wpa_s, version, pos); +} + + +static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "bonjour") == 0) + return p2p_ctrl_service_add_bonjour(wpa_s, pos); + if (os_strcmp(cmd, "upnp") == 0) + return p2p_ctrl_service_add_upnp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s, + char *cmd) +{ + size_t len; + struct wpabuf *query; + int ret; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + query = wpabuf_alloc(len); + if (query == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) { + wpabuf_free(query); + return -1; + } + + ret = wpas_p2p_service_del_bonjour(wpa_s, query); + wpabuf_free(query); + return ret; +} + + +static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 version; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (hexstr2bin(cmd, &version, 1) < 0) + return -1; + + return wpas_p2p_service_del_upnp(wpa_s, version, pos); +} + + +static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "bonjour") == 0) + return p2p_ctrl_service_del_bonjour(wpa_s, pos); + if (os_strcmp(cmd, "upnp") == 0) + return p2p_ctrl_service_del_upnp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + + /* */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + return wpas_p2p_reject(wpa_s, addr); +} + + +static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + int id; + struct wpa_ssid *ssid; + u8 peer[ETH_ALEN]; + + id = atoi(cmd); + pos = os_strstr(cmd, " peer="); + if (pos) { + pos += 6; + if (hwaddr_aton(pos, peer)) + return -1; + } + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "for persistent P2P group", + id); + return -1; + } + + return wpas_p2p_invite(wpa_s, pos ? peer : NULL, ssid, NULL); +} + + +static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + + *pos = '\0'; + pos += 6; + if (hwaddr_aton(pos, peer)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos); + return -1; + } + + pos = os_strstr(pos, " go_dev_addr="); + if (pos) { + pos += 13; + if (hwaddr_aton(pos, go_dev_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", + pos); + return -1; + } + go_dev = go_dev_addr; + } + + return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev); +} + + +static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) +{ + if (os_strncmp(cmd, "persistent=", 11) == 0) + return p2p_ctrl_invite_persistent(wpa_s, cmd + 11); + if (os_strncmp(cmd, "group=", 6) == 0) + return p2p_ctrl_invite_group(wpa_s, cmd + 6); + + return -1; +} + + +static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, + char *cmd, int freq) +{ + int id; + struct wpa_ssid *ssid; + + id = atoi(cmd); + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "for persistent P2P group", + id); + return -1; + } + + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq); +} + + +static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq = 0; + char *pos; + + pos = os_strstr(cmd, "freq="); + if (pos) + freq = atoi(pos + 5); + + if (os_strncmp(cmd, "persistent=", 11) == 0) + return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq); + if (os_strcmp(cmd, "persistent") == 0 || + os_strncmp(cmd, "persistent ", 11) == 0) + return wpas_p2p_group_add(wpa_s, 1, freq); + if (os_strncmp(cmd, "freq=", 5) == 0) + return wpas_p2p_group_add(wpa_s, 0, freq); + + wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", + cmd); + return -1; +} + + +static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN], *addr_ptr; + int next, res; + const struct p2p_peer_info *info; + char *pos, *end; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + struct wpa_ssid *ssid; + + if (!wpa_s->global->p2p) + return -1; + + if (os_strcmp(cmd, "FIRST") == 0) { + addr_ptr = NULL; + next = 0; + } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { + if (hwaddr_aton(cmd + 5, addr) < 0) + return -1; + addr_ptr = addr; + next = 1; + } else { + if (hwaddr_aton(cmd, addr) < 0) + return -1; + addr_ptr = addr; + next = 0; + } + + info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next); + if (info == NULL) + return -1; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, MACSTR "\n" + "pri_dev_type=%s\n" + "device_name=%s\n" + "manufacturer=%s\n" + "model_name=%s\n" + "model_number=%s\n" + "serial_number=%s\n" + "config_methods=0x%x\n" + "dev_capab=0x%x\n" + "group_capab=0x%x\n" + "level=%d\n", + MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, + devtype, sizeof(devtype)), + info->device_name, + info->manufacturer, + info->model_name, + info->model_number, + info->serial_number, + info->config_methods, + info->dev_capab, + info->group_capab, + info->level); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0); + if (ssid) { + res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + res = p2p_get_peer_info_txt(info, pos, end - pos); + if (res < 0) + return pos - buf; + pos += res; + + return pos - buf; +} + + +static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *param; + + if (wpa_s->global->p2p == NULL) + return -1; + + param = os_strchr(cmd, ' '); + if (param == NULL) + return -1; + *param++ = '\0'; + + if (os_strcmp(cmd, "discoverability") == 0) { + p2p_set_client_discoverability(wpa_s->global->p2p, + atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "managed") == 0) { + p2p_set_managed_oper(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "listen_channel") == 0) { + return p2p_set_listen_channel(wpa_s->global->p2p, 81, + atoi(param)); + } + + if (os_strcmp(cmd, "ssid_postfix") == 0) { + return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param, + os_strlen(param)); + } + + if (os_strcmp(cmd, "noa") == 0) { + char *pos; + int count, start, duration; + /* GO NoA parameters: count,start_offset(ms),duration(ms) */ + count = atoi(param); + pos = os_strchr(param, ','); + if (pos == NULL) + return -1; + pos++; + start = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + duration = atoi(pos); + if (count < 0 || count > 255 || start < 0 || duration < 0) + return -1; + if (count == 0 && duration > 0) + return -1; + wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d " + "start=%d duration=%d", count, start, duration); + return wpas_p2p_set_noa(wpa_s, count, start, duration); + } + + if (os_strcmp(cmd, "ps") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1); + + if (os_strcmp(cmd, "oppps") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1); + + if (os_strcmp(cmd, "ctwindow") == 0) + return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param)); + + if (os_strcmp(cmd, "disabled") == 0) { + wpa_s->global->p2p_disabled = atoi(param); + wpa_printf(MSG_DEBUG, "P2P functionality %s", + wpa_s->global->p2p_disabled ? + "disabled" : "enabled"); + if (wpa_s->global->p2p_disabled) { + wpas_p2p_stop_find(wpa_s); + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + p2p_flush(wpa_s->global->p2p); + } + return 0; + } + + if (os_strcmp(cmd, "force_long_sd") == 0) { + wpa_s->force_long_sd = atoi(param); + return 0; + } + + if (os_strcmp(cmd, "peer_filter") == 0) { + u8 addr[ETH_ALEN]; + if (hwaddr_aton(param, addr)) + return -1; + p2p_set_peer_filter(wpa_s->global->p2p, addr); + return 0; + } + + if (os_strcmp(cmd, "cross_connect") == 0) + return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); + + if (os_strcmp(cmd, "go_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_ap_uapsd = 0; + else { + wpa_s->set_ap_uapsd = 1; + wpa_s->ap_uapsd = atoi(param); + } + return 0; + } + + if (os_strcmp(cmd, "client_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_sta_uapsd = 0; + else { + int be, bk, vi, vo; + char *pos; + /* format: BE,BK,VI,VO;max SP Length */ + be = atoi(param); + pos = os_strchr(param, ','); + if (pos == NULL) + return -1; + pos++; + bk = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vi = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vo = atoi(pos); + /* ignore max SP Length for now */ + + wpa_s->set_sta_uapsd = 1; + wpa_s->sta_uapsd = 0; + if (be) + wpa_s->sta_uapsd |= BIT(0); + if (bk) + wpa_s->sta_uapsd |= BIT(1); + if (vi) + wpa_s->sta_uapsd |= BIT(2); + if (vo) + wpa_s->sta_uapsd |= BIT(3); + } + return 0; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", + cmd); + + return -1; +} + + +static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *pos2; + unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; + + if (cmd[0]) { + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + dur1 = atoi(cmd); + + pos2 = os_strchr(pos, ' '); + if (pos2) + *pos2++ = '\0'; + int1 = atoi(pos); + } else + pos2 = NULL; + + if (pos2) { + pos = os_strchr(pos2, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + dur2 = atoi(pos2); + int2 = atoi(pos); + } + + return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); +} + + +static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + unsigned int period = 0, interval = 0; + + if (cmd[0]) { + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + period = atoi(cmd); + interval = atoi(pos); + } + + return wpas_p2p_ext_listen(wpa_s, period, interval); +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_INTERWORKING +static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + + if (hwaddr_aton(dst, bssid)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); + return -1; + } + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, + MAC2STR(bssid)); + return -1; + } + + return interworking_connect(wpa_s, bss); +} + + +static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *pos; +#define MAX_ANQP_INFO_ID 100 + u16 id[MAX_ANQP_INFO_ID]; + size_t num_id = 0; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + pos = dst + used; + while (num_id < MAX_ANQP_INFO_ID) { + id[num_id] = atoi(pos); + if (id[num_id]) + num_id++; + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + if (num_id == 0) + return -1; + + return anqp_send_req(wpa_s, dst_addr, id, num_id); +} +#endif /* CONFIG_INTERWORKING */ + + +static int wpa_supplicant_ctrl_iface_sta_autoconnect( + struct wpa_supplicant *wpa_s, char *cmd) +{ + wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; + return 0; +} + + +static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_signal_info si; + int ret; + + ret = wpa_drv_signal_poll(wpa_s, &si); + if (ret) + return -1; + + ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n" + "NOISE=%d\nFREQUENCY=%u\n", + si.current_signal, si.current_txrate / 1000, + si.current_noise, si.frequency); + if (ret < 0 || (unsigned int) ret > buflen) + return -1; + return ret; +} + + +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, + char *buf, size_t *resp_len) +{ + char *reply; + const int reply_size = 4096; + int ctrl_rsp = 0; + int reply_len; + + if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || + os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + } else { + int level = MSG_DEBUG; + if (os_strcmp(buf, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", + (const u8 *) buf, os_strlen(buf)); + } + + reply = os_malloc(reply_size); + if (reply == NULL) { + *resp_len = 1; + return NULL; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + } else if (os_strcmp(buf, "MIB") == 0) { + reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); + if (reply_len >= 0) { + int res; + res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (os_strncmp(buf, "STATUS", 6) == 0) { + reply_len = wpa_supplicant_ctrl_iface_status( + wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, reply_size); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; + } else if (os_strncmp(buf, "GET ", 4) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4, + reply, reply_size); } else if (os_strcmp(buf, "LOGON") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, FALSE); } else if (os_strcmp(buf, "LOGOFF") == 0) { eapol_sm_notify_logoff(wpa_s->eapol, TRUE); } else if (os_strcmp(buf, "REASSOCIATE") == 0) { - wpa_s->disconnected = 0; - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else { + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } } else if (os_strcmp(buf, "RECONNECT") == 0) { - if (wpa_s->disconnected) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else if (wpa_s->disconnected) { wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -1768,15 +3374,29 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_WPS } else if (os_strcmp(buf, "WPS_PBC") == 0) { - if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL)) + int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL); + if (res == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { - if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8)) + int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8); + if (res == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (res) reply_len = -1; } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, reply, reply_size); + } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_check_pin( + wpa_s, buf + 14, reply, reply_size); + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (wpas_wps_cancel(wpa_s)) + reply_len = -1; #ifdef CONFIG_WPS_OOB } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8)) @@ -1785,9 +3405,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; +#ifdef CONFIG_AP + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( + wpa_s, buf + 11, reply, reply_size); +#endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { - if (wpas_wps_er_start(wpa_s)) + if (wpas_wps_er_start(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) { + if (wpas_wps_er_start(wpa_s, buf + 13)) reply_len = -1; } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { if (wpas_wps_er_stop(wpa_s)) @@ -1796,11 +3424,28 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) { - if (wpas_wps_er_pbc(wpa_s, buf + 11)) + int ret = wpas_wps_er_pbc(wpa_s, buf + 11); + if (ret == -2) { + os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17); + reply_len = 17; + } else if (ret == -3) { + os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18); + reply_len = 18; + } else if (ret == -4) { + os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20); + reply_len = 20; + } else if (ret) reply_len = -1; } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13)) reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s, + buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14)) + reply_len = -1; #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN @@ -1808,6 +3453,113 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_P2P + } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { + if (p2p_ctrl_find(wpa_s, buf + 9)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_FIND") == 0) { + if (p2p_ctrl_find(wpa_s, "")) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) { + wpas_p2p_stop_find(wpa_s); + } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { + reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) { + if (p2p_ctrl_listen(wpa_s, buf + 11)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_LISTEN") == 0) { + if (p2p_ctrl_listen(wpa_s, "")) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) { + if (wpas_p2p_group_remove(wpa_s, buf + 17)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { + if (wpas_p2p_group_add(wpa_s, 0, 0)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { + if (p2p_ctrl_group_add(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { + if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) { + reply_len = p2p_get_passphrase(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) { + reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) { + if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) { + if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) { + wpas_p2p_sd_service_update(wpa_s); + } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) { + if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) { + wpas_p2p_service_flush(wpa_s); + } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) { + if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) { + if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) { + if (p2p_ctrl_reject(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) { + if (p2p_ctrl_invite(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) { + reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply, + reply_size); + } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) { + if (p2p_ctrl_set(wpa_s, buf + 8) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_FLUSH") == 0) { + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->force_long_sd = 0; + if (wpa_s->global->p2p) + p2p_flush(wpa_s->global->p2p); + } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { + if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_CANCEL") == 0) { + if (wpas_p2p_cancel(wpa_s)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) { + if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) { + if (p2p_ctrl_presence_req(wpa_s, "") < 0) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) { + if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { + if (p2p_ctrl_ext_listen(wpa_s, "") < 0) + reply_len = -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { + if (interworking_fetch_anqp(wpa_s) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { + interworking_stop_fetch_anqp(wpa_s); + } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) { + if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") != + NULL) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { + if (ctrl_interworking_connect(wpa_s, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { + if (get_anqp(wpa_s, buf + 9) < 0) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( @@ -1823,17 +3575,39 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "BSSID ", 6) == 0) { if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6)) reply_len = -1; + } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) { + reply_len = wpa_supplicant_ctrl_iface_blacklist( + wpa_s, buf + 9, reply, reply_size); + } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { + reply_len = wpa_supplicant_ctrl_iface_log_level( + wpa_s, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ wpa_s->reassociate = 0; wpa_s->disconnected = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } else if (os_strcmp(buf, "SCAN") == 0) { - wpa_s->scan_req = 2; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + reply_len = -1; + else { + if (!wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else { + wpa_printf(MSG_DEBUG, "Ongoing scan action - " + "reject new request"); + reply_len = os_snprintf(reply, reply_size, + "FAIL-BUSY\n"); + } + } } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); @@ -1869,6 +3643,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) { if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14)) + reply_len = -1; } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( wpa_s->global, reply, reply_size); @@ -1897,6 +3674,30 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "ROAM ", 5) == 0) { if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) reply_len = -1; + } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s, + buf + 17)) + reply_len = -1; +#ifdef CONFIG_TDLS + } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) + reply_len = -1; +#endif /* CONFIG_TDLS */ + } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { + reply_len = wpa_supplicant_signal_poll(wpa_s, reply, + reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2093,8 +3894,11 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *reply; const int reply_size = 2048; int reply_len; + int level = MSG_DEBUG; - wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface", + if (os_strcmp(buf, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX global ctrl_iface", (const u8 *) buf, os_strlen(buf)); reply = os_malloc(reply_size); diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h index 051d99a..88ae6b7 100644 --- a/wpa_supplicant/ctrl_iface.h +++ b/wpa_supplicant/ctrl_iface.h @@ -95,6 +95,21 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv); void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv); /** + * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response + * @wpa_s: Pointer to wpa_supplicant data + * @ssid: Pointer to the network block the reply is for + * @field: field the response is a reply for + * @value: value (ie, password, etc) for @field + * Returns: 0 on success, non-zero on error + * + * Helper function to handle replies to control interface requests. + */ +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const char *field, + const char *value); + +/** * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface * @global: Pointer to global data from wpa_supplicant_init() * Returns: Pointer to private data on success, %NULL on failure diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 84ac760..306a222 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -17,6 +17,9 @@ #include #include #include +#ifdef ANDROID +#include +#endif /* ANDROID */ #include "utils/common.h" #include "utils/eloop.h" @@ -132,7 +135,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; struct ctrl_iface_priv *priv = sock_ctx; - char buf[256]; + char buf[4096]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); @@ -276,6 +279,13 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) buf = os_strdup(wpa_s->conf->ctrl_interface); if (buf == NULL) goto fail; +#ifdef ANDROID + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", + wpa_s->conf->ctrl_interface); + priv->sock = android_get_control_socket(addr.sun_path); + if (priv->sock >= 0) + goto havesock; +#endif /* ANDROID */ if (os_strncmp(buf, "DIR=", 4) == 0) { dir = buf + 4; gid_str = os_strstr(dir, " GROUP="); @@ -398,6 +408,9 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } os_free(fname); +#ifdef ANDROID +havesock: +#endif /* ANDROID */ eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); @@ -637,6 +650,12 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) if (global->params.ctrl_interface == NULL) return priv; +#ifdef ANDROID + priv->sock = android_get_control_socket(global->params.ctrl_interface); + if (priv->sock >= 0) + goto havesock; +#endif /* ANDROID */ + wpa_printf(MSG_DEBUG, "Global control interface '%s'", global->params.ctrl_interface); @@ -685,6 +704,9 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) } } +#ifdef ANDROID +havesock: +#endif /* ANDROID */ eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, NULL); diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile index cfaf58d..d64c65c 100644 --- a/wpa_supplicant/dbus/Makefile +++ b/wpa_supplicant/dbus/Makefile @@ -15,6 +15,7 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +PKG_CONFIG ?= pkg-config CFLAGS += -I../../src -I../../src/utils @@ -38,10 +39,10 @@ CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW CFLAGS += -DCONFIG_CTRL_IFACE_DBUS ifndef DBUS_LIBS -DBUS_LIBS := $(shell pkg-config --libs dbus-1) +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) endif ifndef DBUS_INCLUDE -DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO @@ -49,18 +50,6 @@ DBUS_INCLUDE += $(shell xml2-config --cflags) DBUS_LIBS += $(shell xml2-config --libs) endif -dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) -DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) -DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) -ifeq ($(DBUS_VERSION_MAJOR),) -DBUS_VERSION_MAJOR=0 -endif -ifeq ($(DBUS_VERSION_MINOR),) -DBUS_VERSION_MINOR=0 -endif -DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) -DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) - CFLAGS += $(DBUS_INCLUDE) LIB_OBJS= \ diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c index b3aff40..5f9e64a 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -16,6 +16,7 @@ #include #include "common.h" +#include "wpabuf.h" #include "dbus_dict_helpers.h" @@ -443,11 +444,12 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, /** - * Begin a string array entry in the dict + * Begin an array entry in the dict * * @param iter_dict A valid DBusMessageIter returned from * wpa_dbus_dict_open_write() * @param key The key of the dict item + * @param type The type of the contained data * @param iter_dict_entry A private DBusMessageIter provided by the caller to * be passed to wpa_dbus_dict_end_string_array() * @param iter_dict_val A private DBusMessageIter provided by the caller to @@ -457,12 +459,21 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, * @return TRUE on success, FALSE on failure * */ -dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, - const char *key, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array) +dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict, + const char *key, const char *type, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) { + char array_type[10]; + int err; + + err = os_snprintf(array_type, sizeof(array_type), + DBUS_TYPE_ARRAY_AS_STRING "%s", + type); + if (err < 0 || err > (int) sizeof(array_type)) + return FALSE; + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) return FALSE; @@ -472,20 +483,31 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, if (!dbus_message_iter_open_container(iter_dict_entry, DBUS_TYPE_VARIANT, - DBUS_TYPE_ARRAY_AS_STRING - DBUS_TYPE_STRING_AS_STRING, + array_type, iter_dict_val)) return FALSE; if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, - DBUS_TYPE_BYTE_AS_STRING, - iter_array)) + type, iter_array)) return FALSE; return TRUE; } +dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + return wpa_dbus_dict_begin_array( + iter_dict, key, + DBUS_TYPE_STRING_AS_STRING, + iter_dict_entry, iter_dict_val, iter_array); +} + + /** * Add a single string element to a string array dict entry * @@ -508,23 +530,67 @@ dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, /** - * End a string array dict entry + * Add a single byte array element to a string array dict entry + * + * @param iter_array A valid DBusMessageIter returned from + * wpa_dbus_dict_begin_array()'s iter_array + * parameter -- note that wpa_dbus_dict_begin_array() + * must have been called with "ay" as the type + * @param value The data to be added to the dict entry's array + * @param value_len The length of the data + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, + const u8 *value, + size_t value_len) +{ + DBusMessageIter iter_bytes; + size_t i; + + if (!iter_array || !value) + return FALSE; + + if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &iter_bytes)) + return FALSE; + + for (i = 0; i < value_len; i++) { + if (!dbus_message_iter_append_basic(&iter_bytes, + DBUS_TYPE_BYTE, + &(value[i]))) + return FALSE; + } + + if (!dbus_message_iter_close_container(iter_array, &iter_bytes)) + return FALSE; + + return TRUE; +} + + +/** + * End an array dict entry * * @param iter_dict A valid DBusMessageIter returned from * wpa_dbus_dict_open_write() * @param iter_dict_entry A private DBusMessageIter returned from - * wpa_dbus_dict_end_string_array() + * wpa_dbus_dict_begin_string_array() or + * wpa_dbus_dict_begin_array() * @param iter_dict_val A private DBusMessageIter returned from - * wpa_dbus_dict_end_string_array() + * wpa_dbus_dict_begin_string_array() or + * wpa_dbus_dict_begin_array() * @param iter_array A DBusMessageIter returned from - * wpa_dbus_dict_end_string_array() + * wpa_dbus_dict_begin_string_array() or + * wpa_dbus_dict_begin_array() * @return TRUE on success, FALSE on failure * */ -dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array) +dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) { if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) return FALSE; @@ -583,6 +649,52 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, } +/** + * Convenience function to add an wpabuf binary array to the dict. + * + * @param iter_dict A valid DBusMessageIter returned from + * wpa_dbus_dict_open_write() + * @param key The key of the dict item + * @param items The array of wpabuf structures + * @param num_items The number of strings in the array + * @return TRUE on success, FALSE on failure + * + */ +dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, + const char *key, + const struct wpabuf **items, + const dbus_uint32_t num_items) +{ + DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; + dbus_uint32_t i; + + if (!key) + return FALSE; + if (!items && (num_items != 0)) + return FALSE; + + if (!wpa_dbus_dict_begin_array(iter_dict, key, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &iter_dict_entry, &iter_dict_val, + &iter_array)) + return FALSE; + + for (i = 0; i < num_items; i++) { + if (!wpa_dbus_dict_bin_array_add_element(&iter_array, + wpabuf_head(items[i]), + wpabuf_len(items[i]))) + return FALSE; + } + + if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array)) + return FALSE; + + return TRUE; +} + + /*****************************************************/ /* Stuff for reading dicts */ /*****************************************************/ @@ -593,18 +705,26 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, * @param iter A valid DBusMessageIter pointing to the start of the dict * @param iter_dict (out) A DBusMessageIter to be passed to * wpa_dbus_dict_read_next_entry() + * @error on failure a descriptive error * @return TRUE on success, FALSE on failure * */ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, - DBusMessageIter *iter_dict) + DBusMessageIter *iter_dict, + DBusError *error) { - if (!iter || !iter_dict) + if (!iter || !iter_dict) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "[internal] missing message iterators"); return FALSE; + } if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "unexpected message argument types"); return FALSE; + } dbus_message_iter_recurse(iter, iter_dict); return TRUE; @@ -615,8 +735,7 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, #define BYTE_ARRAY_ITEM_SIZE (sizeof(char)) static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( - DBusMessageIter *iter, int array_type, - struct wpa_dbus_dict_entry *entry) + DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry) { dbus_uint32_t count = 0; dbus_bool_t success = FALSE; @@ -733,6 +852,66 @@ done: } +#define BIN_ARRAY_CHUNK_SIZE 10 +#define BIN_ARRAY_ITEM_SIZE (sizeof(struct wpabuf *)) + +static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( + DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry) +{ + struct wpa_dbus_dict_entry tmpentry; + size_t buflen = 0; + int i; + + if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE) + return FALSE; + + entry->array_type = WPAS_DBUS_TYPE_BINARRAY; + entry->array_len = 0; + entry->binarray_value = NULL; + + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) { + DBusMessageIter iter_array; + + if (entry->array_len == buflen) { + struct wpabuf **newbuf; + + buflen += BIN_ARRAY_CHUNK_SIZE; + + newbuf = os_realloc(entry->binarray_value, + buflen * BIN_ARRAY_ITEM_SIZE); + if (!newbuf) + goto cleanup; + entry->binarray_value = newbuf; + } + + dbus_message_iter_recurse(iter, &iter_array); + if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry) + == FALSE) + goto cleanup; + + entry->binarray_value[entry->array_len] = + wpabuf_alloc_ext_data((u8 *) tmpentry.bytearray_value, + tmpentry.array_len); + if (entry->binarray_value[entry->array_len] == NULL) { + wpa_dbus_dict_entry_clear(&tmpentry); + goto cleanup; + } + entry->array_len++; + dbus_message_iter_next(iter); + } + + return TRUE; + + cleanup: + for (i = 0; i < (int) entry->array_len; i++) + wpabuf_free(entry->binarray_value[i]); + os_free(entry->binarray_value); + entry->array_len = 0; + entry->binarray_value = NULL; + return FALSE; +} + + static dbus_bool_t _wpa_dbus_dict_entry_get_array( DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry) { @@ -748,7 +927,6 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( switch (array_type) { case DBUS_TYPE_BYTE: success = _wpa_dbus_dict_entry_get_byte_array(&iter_array, - array_type, entry); break; case DBUS_TYPE_STRING: @@ -756,6 +934,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( array_type, entry); break; + case DBUS_TYPE_ARRAY: + success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry); default: break; } @@ -915,6 +1095,11 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry) os_free(entry->strarray_value[i]); os_free(entry->strarray_value); break; + case WPAS_DBUS_TYPE_BINARRAY: + for (i = 0; i < entry->array_len; i++) + wpabuf_free(entry->binarray_value[i]); + os_free(entry->binarray_value); + break; } break; } diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h index eb31575..2f6eb45 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -15,6 +15,8 @@ #ifndef DBUS_DICT_HELPERS_H #define DBUS_DICT_HELPERS_H +#include "wpabuf.h" + /* * Adding a dict to a DBusMessage */ @@ -74,7 +76,13 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, const char *value, const dbus_uint32_t value_len); -/* Manual construction and addition of string array elements */ +/* Manual construction and addition of array elements */ +dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict, + const char *key, const char *type, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, const char *key, DBusMessageIter *iter_dict_entry, @@ -84,10 +92,24 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, const char *elem); -dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array); +dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, + const u8 *value, + size_t value_len); + +dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); + +static inline dbus_bool_t +wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array) +{ + return wpa_dbus_dict_end_array(iter_dict, iter_dict_entry, + iter_dict_val, iter_array); +} /* Convenience function to add a whole string list */ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, @@ -95,14 +117,22 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, const char **items, const dbus_uint32_t num_items); +dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, + const char *key, + const struct wpabuf **items, + const dbus_uint32_t num_items); + /* * Reading a dict from a DBusMessage */ +#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100) + struct wpa_dbus_dict_entry { int type; /** the dbus type of the dict entry's value */ int array_type; /** the dbus type of the array elements if the dict - entry value contains an array */ + entry value contains an array, or the special + WPAS_DBUS_TYPE_BINARRAY */ const char *key; /** key of the dict entry */ /** Possible values of the property */ @@ -119,13 +149,15 @@ struct wpa_dbus_dict_entry { double double_value; char *bytearray_value; char **strarray_value; + struct wpabuf **binarray_value; }; dbus_uint32_t array_len; /** length of the array if the dict entry's value contains an array */ }; dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, - DBusMessageIter *iter_dict); + DBusMessageIter *iter_dict, + DBusError *error); dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, struct wpa_dbus_dict_entry *entry); diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index bdfbbac..a871f41 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -17,16 +17,20 @@ #include "includes.h" #include "common.h" +#include "common/ieee802_11_defs.h" #include "wps/wps.h" #include "../config.h" #include "../wpa_supplicant_i.h" #include "../bss.h" +#include "../wpas_glue.h" #include "dbus_new_helpers.h" #include "dbus_dict_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" #include "dbus_common.h" #include "dbus_common_i.h" +#include "dbus_new_handlers_p2p.h" +#include "p2p/p2p.h" /** @@ -42,7 +46,7 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, { struct wpas_dbus_priv *iface; DBusMessage *msg; - DBusMessageIter iter, iter_dict; + DBusMessageIter iter; iface = wpa_s->global->dbus; @@ -61,14 +65,9 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, goto err; if (properties) { - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto err; - - wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, - &iter_dict); - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + if (!wpa_dbus_get_object_properties( + iface, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, &iter)) goto err; } @@ -157,7 +156,7 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, { struct wpas_dbus_priv *iface; DBusMessage *msg; - DBusMessageIter iter, iter_dict; + DBusMessageIter iter; iface = wpa_s->global->dbus; @@ -177,14 +176,9 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, goto err; if (properties) { - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto err; - - wpa_dbus_get_object_properties(iface, bss_obj_path, - WPAS_DBUS_NEW_IFACE_BSS, - &iter_dict); - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + if (!wpa_dbus_get_object_properties(iface, bss_obj_path, + WPAS_DBUS_NEW_IFACE_BSS, + &iter)) goto err; } @@ -304,7 +298,7 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, { struct wpas_dbus_priv *iface; DBusMessage *msg; - DBusMessageIter iter, iter_dict; + DBusMessageIter iter; char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; iface = wpa_s->global->dbus; @@ -330,14 +324,9 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, goto err; if (properties) { - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto err; - - wpa_dbus_get_object_properties(iface, net_obj_path, - WPAS_DBUS_NEW_IFACE_NETWORK, - &iter_dict); - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) + if (!wpa_dbus_get_object_properties( + iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK, + &iter)) goto err; } @@ -394,6 +383,67 @@ void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id) /** + * wpas_dbus_signal_network_request - Indicate that additional information + * (EAP password, etc.) is required to complete the association to this SSID + * @wpa_s: %wpa_supplicant network interface data + * @rtype: The specific additional information required + * @default_text: Optional description of required information + * + * Request additional information or passwords to complete an association + * request. + */ +void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, + const char *default_txt) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + const char *field, *txt = NULL, *net_ptr; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt); + if (field == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NetworkRequest"); + if (msg == NULL) + return; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, ssid->id); + net_ptr = &net_obj_path[0]; + + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &net_ptr)) + goto err; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field)) + goto err; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt)) + goto err; + + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** * wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes * @wpa_s: %wpa_supplicant network interface data * @ssid: configured network which Enabled property has changed @@ -650,381 +700,2146 @@ nomem: #endif /* CONFIG_WPS */ +void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, + int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "Certification"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto nomem; + + if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) || + !wpa_dbus_dict_append_string(&dict_iter, "subject", subject)) + goto nomem; + + if (cert_hash && + !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash)) + goto nomem; + + if (cert && + !wpa_dbus_dict_append_byte_array(&dict_iter, "cert", + wpabuf_head(cert), + wpabuf_len(cert))) + goto nomem; + + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + +#ifdef CONFIG_P2P /** - * wpas_dbus_signal_prop_changed - Signals change of property + * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed * @wpa_s: %wpa_supplicant network interface data - * @property: indicates which property has changed - * - * Sends ProertyChanged signals with path, interface and arguments - * depending on which property has changed. + * @role: role of this device (client or GO) + * Sends signal with i/f name and role as string arguments */ -void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, - enum wpas_dbus_prop property) +void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, + const char *role) { - WPADBusPropertyAccessor getter; - char *prop; - if (wpa_s->dbus_new_path == NULL) - return; /* Skip signal since D-Bus setup is not yet ready */ + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface = wpa_s->global->dbus; + char *ifname = wpa_s->ifname; - switch (property) { - case WPAS_DBUS_PROP_AP_SCAN: - getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan; - prop = "ApScan"; - break; - case WPAS_DBUS_PROP_SCANNING: - getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning; - prop = "Scanning"; - break; - case WPAS_DBUS_PROP_STATE: - getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state; - prop = "State"; - break; - case WPAS_DBUS_PROP_CURRENT_BSS: - getter = (WPADBusPropertyAccessor) - wpas_dbus_getter_current_bss; - prop = "CurrentBSS"; - break; - case WPAS_DBUS_PROP_CURRENT_NETWORK: - getter = (WPADBusPropertyAccessor) - wpas_dbus_getter_current_network; - prop = "CurrentNetwork"; - break; - default: - wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", - __func__, property); + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "GroupFinished"); + if (msg == NULL) return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) { + wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished" + "signal -not enough memory for ifname "); + goto err; } - wpa_dbus_mark_property_changed(wpa_s->global->dbus, - wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, prop); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished" + "signal -not enough memory for role "); + else + dbus_connection_send(iface->con, msg, NULL); + +err: + dbus_message_unref(msg); } /** - * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property - * @wpa_s: %wpa_supplicant network interface data - * @property: indicates which property has changed - * @id: unique BSS identifier + * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events * - * Sends PropertyChanged signals with path, interface, and arguments depending - * on which property has changed. + * @dev_addr - who sent the request or responded to our request. + * @request - Will be 1 if request, 0 for response. + * @status - valid only in case of response + * @config_methods - wps config methods + * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method + * + * Sends following provision discovery related events: + * ProvisionDiscoveryRequestDisplayPin + * ProvisionDiscoveryResponseDisplayPin + * ProvisionDiscoveryRequestEnterPin + * ProvisionDiscoveryResponseEnterPin + * ProvisionDiscoveryPBCRequest + * ProvisionDiscoveryPBCResponse + * + * TODO:: + * ProvisionDiscoveryFailure (timeout case) */ -void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, - enum wpas_dbus_bss_prop property, - unsigned int id) +void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin) { - char path[WPAS_DBUS_OBJECT_PATH_MAX]; - char *prop; + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface; + char *_signal; + int add_pin = 0; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + int error_ret = 1; + char pin[9], *p_pin = NULL; - switch (property) { - case WPAS_DBUS_BSS_PROP_SIGNAL: - prop = "Signal"; - break; - case WPAS_DBUS_BSS_PROP_FREQ: - prop = "Frequency"; - break; - case WPAS_DBUS_BSS_PROP_MODE: - prop = "Mode"; - break; - case WPAS_DBUS_BSS_PROP_PRIVACY: - prop = "Privacy"; - break; - case WPAS_DBUS_BSS_PROP_RATES: - prop = "Rates"; - break; - case WPAS_DBUS_BSS_PROP_WPA: - prop = "WPA"; - break; - case WPAS_DBUS_BSS_PROP_RSN: - prop = "RSN"; - break; - case WPAS_DBUS_BSS_PROP_IES: - prop = "IEs"; - break; - default: - wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", - __func__, property); + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) return; + + if (request || !status) { + if (config_methods & WPS_CONFIG_DISPLAY) + _signal = request ? + "ProvisionDiscoveryRequestDisplayPin" : + "ProvisionDiscoveryResponseEnterPin"; + else if (config_methods & WPS_CONFIG_KEYPAD) + _signal = request ? + "ProvisionDiscoveryRequestEnterPin" : + "ProvisionDiscoveryResponseDisplayPin"; + else if (config_methods & WPS_CONFIG_PUSHBUTTON) + _signal = request ? "ProvisionDiscoveryPBCRequest" : + "ProvisionDiscoveryPBCResponse"; + else + return; /* Unknown or un-supported method */ + } else if (!request && status) + /* Explicit check for failure response */ + _signal = "ProvisionDiscoveryFailure"; + + add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) || + (!request && !status && + (config_methods & WPS_CONFIG_KEYPAD))); + + if (add_pin) { + os_snprintf(pin, sizeof(pin), "%08d", generated_pin); + p_pin = pin; } - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", - wpa_s->dbus_new_path, id); + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal); + if (msg == NULL) + return; - wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, - WPAS_DBUS_NEW_IFACE_BSS, prop); + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, dev_addr)) + goto error; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" + COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); + + path = peer_obj_path; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, + DBUS_TYPE_OBJECT_PATH, + &path)) + goto error; + + if (!request && status) + /* Attach status to ProvisionDiscoveryFailure */ + error_ret = !dbus_message_iter_append_basic(&iter, + DBUS_TYPE_INT32, + &status); + else + error_ret = (add_pin && + !dbus_message_iter_append_basic(&iter, + DBUS_TYPE_STRING, + &p_pin)); + +error: + if (!error_ret) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + + dbus_message_unref(msg); } -/** - * wpas_dbus_signal_debug_level_changed - Signals change of debug param - * @global: wpa_global structure - * - * Sends ProertyChanged signals informing that debug level has changed. - */ -void wpas_dbus_signal_debug_level_changed(struct wpa_global *global) +void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id) { - wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, - WPAS_DBUS_NEW_INTERFACE, - "DebugLevel"); + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(src)); + path = peer_obj_path; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "GONegotiationRequest"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, + &dev_passwd_id)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); } -/** - * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param - * @global: wpa_global structure - * - * Sends ProertyChanged signals informing that debug timestamp has changed. - */ -void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global) +static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + char *group_obj_path) { - wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, - WPAS_DBUS_NEW_INTERFACE, - "DebugTimestamp"); + char group_name[3]; + + if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) + return -1; + + memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); + group_name[2] = '\0'; + + os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s", + wpa_s->dbus_new_path, group_name); + + return 0; } /** - * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param - * @global: wpa_global structure + * wpas_dbus_signal_p2p_group_started - Signals P2P group has + * started. Emitted when a group is successfully started + * irrespective of the role (client/GO) of the current device * - * Sends ProertyChanged signals informing that debug show_keys has changed. + * @wpa_s: %wpa_supplicant network interface data + * @ssid: SSID object + * @client: this device is P2P client + * @network_id: network id of the group started, use instead of ssid->id + * to account for persistent groups */ -void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global) +void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + int client, int network_id) { - wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, - WPAS_DBUS_NEW_INTERFACE, - "DebugShowKeys"); -} + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + iface = wpa_s->parent->global->dbus; -static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc, - void *priv, - WPADBusArgumentFreeFunction priv_free, - const struct wpa_dbus_method_desc *methods, - const struct wpa_dbus_property_desc *properties, - const struct wpa_dbus_signal_desc *signals) + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0) + return; + + /* New interface has been created for this group */ + msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "GroupStarted"); + + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto nomem; + + /* + * In case the device supports creating a separate interface the + * DBus client will need to know the object path for the interface + * object this group was created on, so include it here. + */ + if (!wpa_dbus_dict_append_object_path(&dict_iter, + "interface_object", + wpa_s->dbus_new_path)) + goto nomem; + + if (!wpa_dbus_dict_append_string(&dict_iter, "role", + client ? "client" : "GO")) + goto nomem; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->parent->dbus_new_path, network_id); + + if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + group_obj_path) || + !wpa_dbus_dict_append_object_path(&dict_iter, "network_object", + net_obj_path) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + + +/** + * + * Method to emit GONeogtiation Success or Failure signals based + * on status. + * @status: Status of the GO neg request. 0 for success, other for errors. + */ +void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res) { - int n; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + dbus_int32_t freqs[P2P_MAX_CHANNELS]; + dbus_int32_t *f_array = freqs; - obj_desc->user_data = priv; - obj_desc->user_data_free_func = priv_free; - obj_desc->methods = methods; - obj_desc->properties = properties; - obj_desc->signals = signals; - for (n = 0; properties && properties->dbus_property; properties++) - n++; + iface = wpa_s->global->dbus; - obj_desc->prop_changed_flags = os_zalloc(n); - if (!obj_desc->prop_changed_flags) - wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers", - __func__); + os_memset(freqs, 0, sizeof(freqs)); + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(res->peer_device_addr)); + path = peer_obj_path; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + res->status ? "GONegotiationFailure" : + "GONegotiationSuccess"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto err; + if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + path) || + !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status)) + goto err; + + if (!res->status) { + int i = 0; + int freq_list_num = 0; + + if (res->role_go) { + if (!wpa_dbus_dict_append_byte_array( + &dict_iter, "passphrase", + (const char *) res->passphrase, + sizeof(res->passphrase))) + goto err; + } + + if (!wpa_dbus_dict_append_string(&dict_iter, "role_go", + res->role_go ? "GO" : + "client") || + !wpa_dbus_dict_append_int32(&dict_iter, "frequency", + res->freq) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "ssid", + (const char *) res->ssid, + res->ssid_len) || + !wpa_dbus_dict_append_byte_array(&dict_iter, + "peer_device_addr", + (const char *) + res->peer_device_addr, + ETH_ALEN) || + !wpa_dbus_dict_append_byte_array(&dict_iter, + "peer_interface_addr", + (const char *) + res->peer_interface_addr, + ETH_ALEN) || + !wpa_dbus_dict_append_string(&dict_iter, "wps_method", + p2p_wps_method_text( + res->wps_method))) + goto err; + + for (i = 0; i < P2P_MAX_CHANNELS; i++) { + if (res->freq_list[i]) { + freqs[i] = res->freq_list[i]; + freq_list_num++; + } + } + + if (!wpa_dbus_dict_begin_array(&dict_iter, + "frequency_list", + DBUS_TYPE_INT32_AS_STRING, + &iter_dict_entry, + &iter_dict_val, + &iter_dict_array)) + goto err; + + if (!dbus_message_iter_append_fixed_array(&iter_dict_array, + DBUS_TYPE_INT32, + &f_array, + freq_list_num)) + goto err; + + if (!wpa_dbus_dict_end_array(&dict_iter, + &iter_dict_entry, + &iter_dict_val, + &iter_dict_array)) + goto err; + + if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group", + res->persistent_group) || + !wpa_dbus_dict_append_uint32(&dict_iter, + "peer_config_timeout", + res->peer_config_timeout)) + goto err; + } + + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto err; + + dbus_connection_send(iface->con, msg, NULL); +err: + dbus_message_unref(msg); } -static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { - { "CreateInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_create_interface, +/** + * + * Method to emit Invitation Result signal based on status and + * bssid + * @status: Status of the Invite request. 0 for success, other + * for errors + * @bssid : Basic Service Set Identifier + */ +void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + + wpa_printf(MSG_INFO, "%s\n", __func__); + + iface = wpa_s->global->dbus; + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "InvitationResult"); + + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto nomem; + + if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status)) + goto nomem; + if (bssid) { + if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID", + (const char *) bssid, + ETH_ALEN)) + goto nomem; + } + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + + +/** + * + * Method to emit a signal for a peer joining the group. + * The signal will carry path to the group member object + * constructed using p2p i/f addr used for connecting. + * + * @wpa_s: %wpa_supplicant network interface data + * @member_addr: addr (p2p i/f) of the peer joining the group + */ +void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, + const u8 *member) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" + COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(member)); + + msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, + WPAS_DBUS_NEW_IFACE_P2P_GROUP, + "PeerJoined"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + path = groupmember_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + goto err; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * + * Method to emit a signal for a peer disconnecting the group. + * The signal will carry path to the group member object + * constructed using p2p i/f addr used for connecting. + * + * @wpa_s: %wpa_supplicant network interface data + * @member_addr: addr (p2p i/f) of the peer joining the group + */ +void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *member) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" + COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(member)); + + msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, + WPAS_DBUS_NEW_IFACE_P2P_GROUP, + "PeerDisconnected"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + path = groupmember_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + goto err; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected " + "signal"); + dbus_message_unref(msg); +} + + +/** + * + * Method to emit a signal for a service discovery request. + * The signal will carry station address, frequency, dialog token, + * update indicator and it tlvs + * + * @wpa_s: %wpa_supplicant network interface data + * @sa: station addr (p2p i/f) of the peer + * @dialog_token: service discovery request dialog token + * @update_indic: service discovery request update indicator + * @tlvs: service discovery request genrated byte array of tlvs + * @tlvs_len: service discovery request tlvs length + */ +void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "ServiceDiscoveryRequest"); + if (msg == NULL) + return; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + goto error; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" + COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa)); + + path = peer_obj_path; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto error; + + + if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + path) || + !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) || + !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token", + dialog_token) || + !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator", + update_indic) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs", + (const char *) tlvs, + tlvs_len) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto error; + + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); + return; +error: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * + * Method to emit a signal for a service discovery response. + * The signal will carry station address, update indicator and it + * tlvs + * + * @wpa_s: %wpa_supplicant network interface data + * @sa: station addr (p2p i/f) of the peer + * @update_indic: service discovery request update indicator + * @tlvs: service discovery request genrated byte array of tlvs + * @tlvs_len: service discovery request tlvs length + */ +void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "ServiceDiscoveryResponse"); + if (msg == NULL) + return; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + goto error; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" + COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa)); + + path = peer_obj_path; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto error; + + if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + path) || + !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator", + update_indic) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs", + (const char *) tlvs, + tlvs_len) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto error; + + + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); + return; +error: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + +/** + * wpas_dbus_signal_persistent_group - Send a persistent group related + * event signal + * @wpa_s: %wpa_supplicant network interface data + * @id: new persistent group id + * @sig_name: signal name - PersistentGroupAdded, PersistentGroupRemoved + * @properties: determines if add second argument with object properties + * + * Notify listeners about an event related to persistent groups. + */ +static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, + int id, const char *sig_name, + int properties) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", + wpa_s->dbus_new_path, id); + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + sig_name); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + path = pgrp_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + goto err; + + if (properties) { + if (!wpa_dbus_get_object_properties( + iface, pgrp_obj_path, + WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)) + goto err; + } + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_persistent_group_added - Send a persistent_group + * added signal + * @wpa_s: %wpa_supplicant network interface data + * @id: new persistent group id + * + * Notify listeners about addition of a new persistent group. + */ +static void wpas_dbus_signal_persistent_group_added( + struct wpa_supplicant *wpa_s, int id) +{ + wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupAdded", + TRUE); +} + + +/** + * wpas_dbus_signal_persistent_group_removed - Send a persistent_group + * removed signal + * @wpa_s: %wpa_supplicant network interface data + * @id: persistent group id + * + * Notify listeners about removal of a persistent group. + */ +static void wpas_dbus_signal_persistent_group_removed( + struct wpa_supplicant *wpa_s, int id) +{ + wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupRemoved", + FALSE); +} + + +/** + * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event + * @wpa_s: %wpa_supplicant network interface data + * + * Sends Event dbus signal with name "fail" and dictionary containing + * "msg" field with fail message number (int32) as arguments + */ +void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char *key = "fail"; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "WpsFailed"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) || + !wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) || + !wpa_dbus_dict_append_int16(&dict_iter, "config_error", + fail->config_error) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + +#endif /*CONFIG_P2P*/ + + +/** + * wpas_dbus_signal_prop_changed - Signals change of property + * @wpa_s: %wpa_supplicant network interface data + * @property: indicates which property has changed + * + * Sends PropertyChanged signals with path, interface and arguments + * depending on which property has changed. + */ +void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_prop property) +{ + char *prop; + + if (wpa_s->dbus_new_path == NULL) + return; /* Skip signal since D-Bus setup is not yet ready */ + + switch (property) { + case WPAS_DBUS_PROP_AP_SCAN: + prop = "ApScan"; + break; + case WPAS_DBUS_PROP_SCANNING: + prop = "Scanning"; + break; + case WPAS_DBUS_PROP_STATE: + prop = "State"; + break; + case WPAS_DBUS_PROP_CURRENT_BSS: + prop = "CurrentBSS"; + break; + case WPAS_DBUS_PROP_CURRENT_NETWORK: + prop = "CurrentNetwork"; + break; + case WPAS_DBUS_PROP_BSSS: + prop = "BSSs"; + break; + case WPAS_DBUS_PROP_CURRENT_AUTH_MODE: + prop = "CurrentAuthMode"; + break; + default: + wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", + __func__, property); + return; + } + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, + wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, prop); +} + + +/** + * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property + * @wpa_s: %wpa_supplicant network interface data + * @property: indicates which property has changed + * @id: unique BSS identifier + * + * Sends PropertyChanged signals with path, interface, and arguments depending + * on which property has changed. + */ +void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_bss_prop property, + unsigned int id) +{ + char path[WPAS_DBUS_OBJECT_PATH_MAX]; + char *prop; + + switch (property) { + case WPAS_DBUS_BSS_PROP_SIGNAL: + prop = "Signal"; + break; + case WPAS_DBUS_BSS_PROP_FREQ: + prop = "Frequency"; + break; + case WPAS_DBUS_BSS_PROP_MODE: + prop = "Mode"; + break; + case WPAS_DBUS_BSS_PROP_PRIVACY: + prop = "Privacy"; + break; + case WPAS_DBUS_BSS_PROP_RATES: + prop = "Rates"; + break; + case WPAS_DBUS_BSS_PROP_WPA: + prop = "WPA"; + break; + case WPAS_DBUS_BSS_PROP_RSN: + prop = "RSN"; + break; + case WPAS_DBUS_BSS_PROP_IES: + prop = "IEs"; + break; + default: + wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", + __func__, property); + return; + } + + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, + WPAS_DBUS_NEW_IFACE_BSS, prop); +} + + +/** + * wpas_dbus_signal_debug_level_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends PropertyChanged signals informing that debug level has changed. + */ +void wpas_dbus_signal_debug_level_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugLevel"); +} + + +/** + * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends PropertyChanged signals informing that debug timestamp has changed. + */ +void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugTimestamp"); +} + + +/** + * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param + * @global: wpa_global structure + * + * Sends PropertyChanged signals informing that debug show_keys has changed. + */ +void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global) +{ + wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_INTERFACE, + "DebugShowKeys"); +} + + +static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc, + void *priv, + WPADBusArgumentFreeFunction priv_free, + const struct wpa_dbus_method_desc *methods, + const struct wpa_dbus_property_desc *properties, + const struct wpa_dbus_signal_desc *signals) +{ + int n; + + obj_desc->user_data = priv; + obj_desc->user_data_free_func = priv_free; + obj_desc->methods = methods; + obj_desc->properties = properties; + obj_desc->signals = signals; + + for (n = 0; properties && properties->dbus_property; properties++) + n++; + + obj_desc->prop_changed_flags = os_zalloc(n); + if (!obj_desc->prop_changed_flags) + wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers", + __func__); +} + + +static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { + { "CreateInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_create_interface, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "GetInterface", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_get_interface, + { + { "ifname", "s", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, NULL, { END_ARGS } } +}; + +static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { + { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s", + wpas_dbus_getter_debug_level, + wpas_dbus_setter_debug_level + }, + { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", + wpas_dbus_getter_debug_timestamp, + wpas_dbus_setter_debug_timestamp + }, + { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", + wpas_dbus_getter_debug_show_keys, + wpas_dbus_setter_debug_show_keys + }, + { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", + wpas_dbus_getter_interfaces, + NULL + }, + { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", + wpas_dbus_getter_eap_methods, + NULL + }, + { NULL, NULL, NULL, NULL, NULL } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { + { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "field", "s", ARG_OUT }, + { "text", "s", ARG_OUT }, + END_ARGS + } + }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_ctrl_iface_init - Initialize dbus control interface + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success or -1 on failure + * + * Initialize the dbus control interface for wpa_supplicantand and start + * receiving commands from external programs over the bus. + */ +int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) +{ + struct wpa_dbus_object_desc *obj_desc; + int ret; + + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + return -1; + } + + wpas_dbus_register(obj_desc, priv->global, NULL, + wpas_dbus_global_methods, + wpas_dbus_global_properties, + wpas_dbus_global_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'", + WPAS_DBUS_NEW_PATH); + ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, + WPAS_DBUS_NEW_SERVICE, + obj_desc); + if (ret < 0) + free_dbus_object_desc(obj_desc); + else + priv->dbus_new_initialized = 1; + + return ret; +} + + +/** + * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for + * wpa_supplicant + * @iface: Pointer to dbus private data from wpas_dbus_init() + * + * Deinitialize the dbus control interface that was initialized with + * wpas_dbus_ctrl_iface_init(). + */ +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface) +{ + if (!iface->dbus_new_initialized) + return; + wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'", + WPAS_DBUS_NEW_PATH); + dbus_connection_unregister_object_path(iface->con, + WPAS_DBUS_NEW_PATH); +} + + +static void wpa_dbus_free(void *ptr) +{ + os_free(ptr); +} + + +static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = { + { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}", + wpas_dbus_getter_network_properties, + wpas_dbus_setter_network_properties + }, + { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", + wpas_dbus_getter_enabled, + wpas_dbus_setter_enabled + }, + { NULL, NULL, NULL, NULL, NULL } +}; + + +static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = { + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_register_network - Register a configured network with dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: network configuration data + * Returns: 0 on success, -1 on failure + * + * Registers network representing object with dbus + */ +int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + struct network_handler_args *arg; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + +#ifdef CONFIG_P2P + /* + * If it is a persistent group register it as such. + * This is to handle cases where an interface is being initialized + * with a list of networks read from config. + */ + if (network_is_persistent_group(ssid)) + return wpas_dbus_register_persistent_group(wpa_s, ssid); +#endif /* CONFIG_P2P */ + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, ssid->id); + + wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'", + net_obj_path); + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct network_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for method"); + goto err; + } + + arg->wpa_s = wpa_s; + arg->ssid = ssid; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_network_properties, + wpas_dbus_network_signals); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + wpas_dbus_signal_network_added(wpa_s, ssid->id); + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + + +/** + * wpas_dbus_unregister_network - Unregister a configured network from dbus + * @wpa_s: wpa_supplicant interface structure + * @nid: network id + * Returns: 0 on success, -1 on failure + * + * Unregisters network representing object from dbus + */ +int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) +{ + struct wpas_dbus_priv *ctrl_iface; + char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; + struct wpa_ssid *ssid; + + ssid = wpa_config_get_network(wpa_s->conf, nid); + +#ifdef CONFIG_P2P + /* If it is a persistent group unregister it as such */ + if (ssid && network_is_persistent_group(ssid)) + return wpas_dbus_unregister_persistent_group(wpa_s, nid); +#endif /* CONFIG_P2P */ + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", + wpa_s->dbus_new_path, nid); + + wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'", + net_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path); + + if (!ret) + wpas_dbus_signal_network_removed(wpa_s, nid); + + return ret; +} + + +static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { + { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", + wpas_dbus_getter_bss_ssid, + NULL + }, + { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", + wpas_dbus_getter_bss_bssid, + NULL + }, + { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", + wpas_dbus_getter_bss_privacy, + NULL + }, + { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", + wpas_dbus_getter_bss_mode, + NULL + }, + { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", + wpas_dbus_getter_bss_signal, + NULL + }, + { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", + wpas_dbus_getter_bss_frequency, + NULL + }, + { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", + wpas_dbus_getter_bss_rates, + NULL + }, + { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + wpas_dbus_getter_bss_wpa, + NULL + }, + { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + wpas_dbus_getter_bss_rsn, + NULL + }, + { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", + wpas_dbus_getter_bss_ies, + NULL + }, + { NULL, NULL, NULL, NULL, NULL } +}; + + +static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = { + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus + * @wpa_s: wpa_supplicant interface structure + * @bssid: scanned network bssid + * @id: unique BSS identifier + * Returns: 0 on success, -1 on failure + * + * Unregisters BSS representing object from dbus + */ +int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + struct wpas_dbus_priv *ctrl_iface; + char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'", + bss_obj_path); + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) { + wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s", + bss_obj_path); + return -1; + } + + wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); + + return 0; +} + + +/** + * wpas_dbus_register_bss - Register a scanned BSS with dbus + * @wpa_s: wpa_supplicant interface structure + * @bssid: scanned network bssid + * @id: unique BSS identifier + * Returns: 0 on success, -1 on failure + * + * Registers BSS representing object with dbus + */ +int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, + u8 bssid[ETH_ALEN], unsigned int id) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + struct bss_handler_args *arg; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", + wpa_s->dbus_new_path, id); + + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + arg = os_zalloc(sizeof(struct bss_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for handler"); + goto err; + } + arg->wpa_s = wpa_s; + arg->id = id; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_bss_properties, + wpas_dbus_bss_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'", + bss_obj_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path, + wpa_s->ifname, obj_desc)) { + wpa_printf(MSG_ERROR, + "Cannot register BSSID dbus object %s.", + bss_obj_path); + goto err; + } + + wpas_dbus_signal_bss_added(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + + +static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { + { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_scan, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, + { + END_ARGS + } + }, + { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_add_network, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks, + { + END_ARGS + } + }, + { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_select_network, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_network_reply, + { + { "path", "o", ARG_IN }, + { "field", "s", ARG_IN }, + { "value", "s", ARG_IN }, + END_ARGS + } + }, + { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, + { + { "name", "s", ARG_IN }, + { "data", "ay", ARG_IN }, + END_ARGS + } + }, + { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_get_blob, + { + { "name", "s", ARG_IN }, + { "data", "ay", ARG_OUT }, + END_ARGS + } + }, + { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob, + { + { "name", "s", ARG_IN }, + END_ARGS + } + }, +#ifdef CONFIG_WPS + { "Start", WPAS_DBUS_NEW_IFACE_WPS, + (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, + { + { "args", "a{sv}", ARG_IN }, + { "output", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_find, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find, + { + END_ARGS + } + }, + { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen, + { + { "timeout", "i", ARG_IN }, + END_ARGS + } + }, + { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req, + { + { "peer", "o", ARG_IN }, + { "config_method", "s", ARG_IN }, + END_ARGS + } + }, + { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect, + { + { "args", "a{sv}", ARG_IN }, + { "generated_pin", "s", ARG_OUT }, + END_ARGS + } + }, + { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect, + { + END_ARGS + } + }, + { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer, + { + { "peer", "o", ARG_IN }, + END_ARGS + } + }, + { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush, + { + END_ARGS + } + }, + { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service, + { + END_ARGS + } + }, + { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req, + { + { "args", "t", ARG_IN }, + END_ARGS + } + }, + { "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update, + { + END_ARGS + } + }, + { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external, + { + { "arg", "i", ARG_IN }, + END_ARGS + } + }, + { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external, + { + { "arg", "i", ARG_IN }, + END_ARGS + } + }, + { "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group, + { + { "args", "a{sv}", ARG_IN }, + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "RemovePersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) wpas_dbus_handler_remove_persistent_group, + { + { "path", "o", ARG_IN }, + END_ARGS + } + }, + { "RemoveAllPersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + (WPADBusMethodHandler) + wpas_dbus_handler_remove_all_persistent_groups, + { + END_ARGS + } + }, +#endif /* CONFIG_P2P */ + { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss, + { + { "age", "u", ARG_IN }, + END_ARGS + } + }, + { NULL, NULL, NULL, { END_ARGS } } +}; + +static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { + { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}", + wpas_dbus_getter_capabilities, + NULL + }, + { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_state, + NULL + }, + { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_scanning, + NULL + }, + { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_ap_scan, + wpas_dbus_setter_ap_scan + }, + { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_bss_expire_age, + wpas_dbus_setter_bss_expire_age + }, + { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_bss_expire_count, + wpas_dbus_setter_bss_expire_count + }, + { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_country, + wpas_dbus_setter_country + }, + { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_ifname, + NULL + }, + { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_driver, + NULL + }, + { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_bridge_ifname, + NULL + }, + { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", + wpas_dbus_getter_current_bss, + NULL + }, + { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", + wpas_dbus_getter_current_network, + NULL + }, + { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_current_auth_mode, + NULL + }, + { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}", + wpas_dbus_getter_blobs, + NULL + }, + { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", + wpas_dbus_getter_bsss, + NULL + }, + { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", + wpas_dbus_getter_networks, + NULL + }, + { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_fast_reauth, + wpas_dbus_setter_fast_reauth + }, +#ifdef CONFIG_WPS + { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", + wpas_dbus_getter_process_credentials, + wpas_dbus_setter_process_credentials + }, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", + wpas_dbus_getter_p2p_device_config, + wpas_dbus_setter_p2p_device_config + }, + { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", + wpas_dbus_getter_p2p_peers, + NULL + }, + { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s", + wpas_dbus_getter_p2p_role, + NULL + }, + { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", + wpas_dbus_getter_p2p_group, + NULL + }, + { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", + wpas_dbus_getter_p2p_peergo, + NULL + }, + { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", + wpas_dbus_getter_persistent_groups, + NULL + }, +#endif /* CONFIG_P2P */ + { NULL, NULL, NULL, NULL, NULL } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { + { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "success", "b", ARG_OUT }, + END_ARGS + } + }, + { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#ifdef CONFIG_WPS + { "Event", WPAS_DBUS_NEW_IFACE_WPS, + { + { "name", "s", ARG_OUT }, + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "Credentials", WPAS_DBUS_NEW_IFACE_WPS, + { + { "credentials", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS, { - { "args", "a{sv}", ARG_IN }, - { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, - { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "path", "o", ARG_IN }, + { "states", "a{ss}", ARG_OUT }, END_ARGS } }, - { "GetInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_interface, + { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "ifname", "s", ARG_IN }, { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, - { NULL, NULL, NULL, { END_ARGS } } -}; - -static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { - { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level, - (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level, - RW + { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } }, - { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp, - (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp, - RW + { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "pin", "s", ARG_OUT }, + END_ARGS + } }, - { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys, - (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys, - RW + { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "pin", "s", ARG_OUT }, + END_ARGS + } }, - { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", - (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces, - NULL, - R + { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } }, - { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", - (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods, - NULL, - R + { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } }, - { NULL, NULL, NULL, NULL, NULL, 0 } -}; - -static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { - { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE, + { "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "status", "i", ARG_OUT }, + END_ARGS + } + }, + { "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "path", "o", ARG_OUT }, { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, - { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE, + { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + END_ARGS + } + }, + { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "status", "i", ARG_OUT }, + END_ARGS + } + }, + { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, + { "dev_passwd_id", "i", ARG_OUT }, END_ARGS } }, - { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE, + { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "properties", "a{sv}", ARG_OUT }, + { "invite_result", "a{sv}", ARG_OUT }, END_ARGS } }, - { NULL, NULL, { END_ARGS } } -}; - - -/** - * wpas_dbus_ctrl_iface_init - Initialize dbus control interface - * @global: Pointer to global data from wpa_supplicant_init() - * Returns: 0 on success or -1 on failure - * - * Initialize the dbus control interface for wpa_supplicantand and start - * receiving commands from external programs over the bus. - */ -int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) -{ - struct wpa_dbus_object_desc *obj_desc; - int ret; - - obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); - if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); - return -1; - } - - wpas_dbus_register(obj_desc, priv->global, NULL, - wpas_dbus_global_methods, - wpas_dbus_global_properties, - wpas_dbus_global_signals); - - wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'", - WPAS_DBUS_NEW_PATH); - ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, - WPAS_DBUS_NEW_SERVICE, - obj_desc); - if (ret < 0) - free_dbus_object_desc(obj_desc); - else - priv->dbus_new_initialized = 1; - - return ret; -} - - -/** - * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for - * wpa_supplicant - * @iface: Pointer to dbus private data from wpas_dbus_init() - * - * Deinitialize the dbus control interface that was initialized with - * wpas_dbus_ctrl_iface_init(). - */ -void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface) -{ - if (!iface->dbus_new_initialized) - return; - wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'", - WPAS_DBUS_NEW_PATH); - dbus_connection_unregister_object_path(iface->con, - WPAS_DBUS_NEW_PATH); -} - - -static void wpa_dbus_free(void *ptr) -{ - os_free(ptr); -} - - -static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = { - { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties, - (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties, - RW + { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "ifname", "s", ARG_OUT }, + { "role", "s", ARG_OUT }, + END_ARGS + } }, - { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_enabled, - (WPADBusPropertyAccessor) wpas_dbus_setter_enabled, - RW + { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "sd_request", "a{sv}", ARG_OUT }, + END_ARGS + } }, - { NULL, NULL, NULL, NULL, NULL, 0 } -}; - - -static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = { - { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK, + { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "sd_response", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { + { "path", "o", ARG_OUT }, { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, + { "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "name", "s", ARG_OUT }, + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_P2P */ + { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "certification", "a{sv}", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; -/** - * wpas_dbus_register_network - Register a configured network with dbus - * @wpa_s: wpa_supplicant interface structure - * @ssid: network configuration data - * Returns: 0 on success, -1 on failure - * - * Registers network representing object with dbus - */ -int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) { - struct wpas_dbus_priv *ctrl_iface; - struct wpa_dbus_object_desc *obj_desc; - struct network_handler_args *arg; - char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + struct wpa_dbus_object_desc *obj_desc = NULL; + struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; + int next; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) - return 0; - ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; - os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", - wpa_s->dbus_new_path, ssid->id); + /* Create and set the interface's object path */ + wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (wpa_s->dbus_new_path == NULL) + return -1; + next = ctrl_iface->next_objid++; + os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX, + WPAS_DBUS_NEW_PATH_INTERFACES "/%u", + next); - wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'", - net_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { wpa_printf(MSG_ERROR, "Not enough memory " @@ -1032,206 +2847,212 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, goto err; } - /* allocate memory for handlers arguments */ - arg = os_zalloc(sizeof(struct network_handler_args)); - if (!arg) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create arguments for method"); - goto err; - } - - arg->wpa_s = wpa_s; - arg->ssid = ssid; - - wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, - wpas_dbus_network_properties, - wpas_dbus_network_signals); + wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods, + wpas_dbus_interface_properties, + wpas_dbus_interface_signals); - if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path, + wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'", + wpa_s->dbus_new_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, + wpa_s->dbus_new_path, wpa_s->ifname, obj_desc)) goto err; - wpas_dbus_signal_network_added(wpa_s, ssid->id); + wpas_dbus_signal_interface_added(wpa_s); return 0; err: + os_free(wpa_s->dbus_new_path); + wpa_s->dbus_new_path = NULL; free_dbus_object_desc(obj_desc); return -1; } -/** - * wpas_dbus_unregister_network - Unregister a configured network from dbus - * @wpa_s: wpa_supplicant interface structure - * @nid: network id - * Returns: 0 on success, -1 on failure - * - * Unregisters network representing object from dbus - */ -int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) +int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) { struct wpas_dbus_priv *ctrl_iface; - char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; - int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s == NULL || wpa_s->global == NULL) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; - os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", - wpa_s->dbus_new_path, nid); + wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", + wpa_s->dbus_new_path); + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, + wpa_s->dbus_new_path)) + return -1; - wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'", - net_obj_path); - ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path); + wpas_dbus_signal_interface_removed(wpa_s); - if (!ret) - wpas_dbus_signal_network_removed(wpa_s, nid); + os_free(wpa_s->dbus_new_path); + wpa_s->dbus_new_path = NULL; - return ret; + return 0; } +#ifdef CONFIG_P2P -static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { - { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid, - NULL, - R +static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { + { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_device_name, + NULL }, - { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid, - NULL, - R - }, - { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy, - NULL, - R + { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_primary_device_type, + NULL }, - { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode, - NULL, - R + { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q", + wpas_dbus_getter_p2p_peer_config_method, + NULL }, - { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal, - NULL, - R + { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i", + wpas_dbus_getter_p2p_peer_level, + NULL }, - { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency, - NULL, - R + { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", + wpas_dbus_getter_p2p_peer_device_capability, + NULL }, - { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates, - NULL, - R + { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", + wpas_dbus_getter_p2p_peer_group_capability, + NULL }, - { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa, - NULL, - R + { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", + wpas_dbus_getter_p2p_peer_secondary_device_types, + NULL }, - { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn, - NULL, - R + { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", + wpas_dbus_getter_p2p_peer_vendor_extension, + NULL }, - { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies, - NULL, - R + { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_ies, + NULL }, - { NULL, NULL, NULL, NULL, NULL, 0 } + { NULL, NULL, NULL, NULL, NULL } }; +static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { -static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = { - { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS, - { - { "properties", "a{sv}", ARG_OUT }, - END_ARGS - } - }, { NULL, NULL, { END_ARGS } } }; - /** - * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus - * @wpa_s: wpa_supplicant interface structure - * @bssid: scanned network bssid - * @id: unique BSS identifier - * Returns: 0 on success, -1 on failure + * wpas_dbus_signal_peer - Send a peer related event signal + * @wpa_s: %wpa_supplicant network interface data + * @dev: peer device object + * @interface: name of the interface emitting this signal. + * In case of peer objects, it would be emitted by either + * the "interface object" or by "peer objects" + * @sig_name: signal name - DeviceFound * - * Unregisters BSS representing object from dbus + * Notify listeners about event related with newly found p2p peer device */ -int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, - u8 bssid[ETH_ALEN], unsigned int id) +static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const char *interface, + const char *sig_name) { - struct wpas_dbus_priv *ctrl_iface; - char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) - return 0; - ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL) - return 0; + if (iface == NULL) + return; - os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", - wpa_s->dbus_new_path, id); + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); - wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'", - bss_obj_path); - if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) { - wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s", - bss_obj_path); - return -1; - } + msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface, + sig_name); + if (msg == NULL) + return; - wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path); + dbus_message_iter_init_append(msg, &iter); + path = peer_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + goto err; - return 0; + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); + return; + +err: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); } /** - * wpas_dbus_register_bss - Register a scanned BSS with dbus + * wpas_dbus_signal_peer_found - Send a peer found signal + * @wpa_s: %wpa_supplicant network interface data + * @dev: peer device object + * + * Notify listeners about find a p2p peer device found + */ +void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + wpas_dbus_signal_peer(wpa_s, dev_addr, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "DeviceFound"); +} + +/** + * wpas_dbus_signal_peer_lost - Send a peer lost signal + * @wpa_s: %wpa_supplicant network interface data + * @dev: peer device object + * + * Notify listeners about lost a p2p peer device + */ +void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + wpas_dbus_signal_peer(wpa_s, dev_addr, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "DeviceLost"); +} + +/** + * wpas_dbus_register_peer - Register a discovered peer object with dbus * @wpa_s: wpa_supplicant interface structure - * @bssid: scanned network bssid - * @id: unique BSS identifier + * @ssid: network configuration data * Returns: 0 on success, -1 on failure * - * Registers BSS representing object with dbus + * Registers network representing object with dbus */ -int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, - u8 bssid[ETH_ALEN], unsigned int id) +int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) { struct wpas_dbus_priv *ctrl_iface; struct wpa_dbus_object_desc *obj_desc; - char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; - struct bss_handler_args *arg; + struct peer_handler_args *arg; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; /* Do nothing if the control interface is not turned on */ if (wpa_s == NULL || wpa_s->global == NULL) return 0; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; - os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u", - wpa_s->dbus_new_path, id); + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); + wpa_printf(MSG_INFO, "dbus: Register peer object '%s'", + peer_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { wpa_printf(MSG_ERROR, "Not enough memory " @@ -1239,30 +3060,25 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, goto err; } - arg = os_zalloc(sizeof(struct bss_handler_args)); + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct peer_handler_args)); if (!arg) { wpa_printf(MSG_ERROR, "Not enough memory " - "to create arguments for handler"); + "to create arguments for method"); goto err; } + arg->wpa_s = wpa_s; - arg->id = id; + os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN); - wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, - wpas_dbus_bss_properties, - wpas_dbus_bss_signals); + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, + NULL, + wpas_dbus_p2p_peer_properties, + wpas_dbus_p2p_peer_signals); - wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'", - bss_obj_path); - if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path, - wpa_s->ifname, obj_desc)) { - wpa_printf(MSG_ERROR, - "Cannot register BSSID dbus object %s.", - bss_obj_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path, + wpa_s->ifname, obj_desc)) goto err; - } - - wpas_dbus_signal_bss_added(wpa_s, bss_obj_path); return 0; @@ -1271,240 +3087,231 @@ err: return -1; } +/** + * wpas_dbus_unregister_peer - Unregister a peer object with dbus + * @wpa_s: wpa_supplicant interface structure + * @dev_addr: p2p device addr + * Returns: 0 on success, -1 on failure + * + * Registers network representing object with dbus + */ +int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + struct wpas_dbus_priv *ctrl_iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL || + wpa_s->dbus_new_path == NULL) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; -static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { - { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_scan, - { - { "args", "a{sv}", ARG_IN }, - END_ARGS - } - }, - { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, - { - END_ARGS - } - }, - { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_network, - { - { "args", "a{sv}", ARG_IN }, - { "path", "o", ARG_OUT }, - END_ARGS - } - }, - { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, - { - { "path", "o", ARG_IN }, - END_ARGS - } - }, - { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_select_network, - { - { "path", "o", ARG_IN }, - END_ARGS - } - }, - { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, - { - { "name", "s", ARG_IN }, - { "data", "ay", ARG_IN }, - END_ARGS - } - }, - { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_blob, - { - { "name", "s", ARG_IN }, - { "data", "ay", ARG_OUT }, - END_ARGS - } - }, - { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob, - { - { "name", "s", ARG_IN }, - END_ARGS - } - }, -#ifdef CONFIG_WPS - { "Start", WPAS_DBUS_NEW_IFACE_WPS, - (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, - { - { "args", "a{sv}", ARG_IN }, - { "output", "a{sv}", ARG_OUT }, - END_ARGS - } - }, -#endif /* CONFIG_WPS */ - { NULL, NULL, NULL, { END_ARGS } } -}; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); -static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { - { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities, - NULL, R - }, - { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_state, - NULL, R - }, - { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_scanning, - NULL, R - }, - { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", - (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan, - (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan, - RW - }, - { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_ifname, - NULL, R - }, - { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_driver, - NULL, R - }, - { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname, - NULL, R - }, - { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", - (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss, - NULL, R - }, - { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", - (WPADBusPropertyAccessor) wpas_dbus_getter_current_network, - NULL, R - }, - { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}", - (WPADBusPropertyAccessor) wpas_dbus_getter_blobs, - NULL, R - }, - { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", - (WPADBusPropertyAccessor) wpas_dbus_getter_bsss, - NULL, R - }, - { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", - (WPADBusPropertyAccessor) wpas_dbus_getter_networks, - NULL, R - }, -#ifdef CONFIG_WPS - { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials, - (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials, - RW - }, -#endif /* CONFIG_WPS */ - { NULL, NULL, NULL, NULL, NULL, 0 } -}; + wpa_printf(MSG_INFO, "dbus: Unregister peer object '%s'", + peer_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path); -static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { - { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "success", "b", ARG_OUT }, - END_ARGS - } - }, - { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - { "properties", "a{sv}", ARG_OUT }, - END_ARGS - } + return ret; +} + + +static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = { + { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao", + wpas_dbus_getter_p2p_group_members, + NULL }, - { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - END_ARGS - } + { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o", + wpas_dbus_getter_p2p_group, + NULL }, - { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "name", "s", ARG_OUT }, - END_ARGS - } + { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", + wpas_dbus_getter_p2p_role, + NULL }, - { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "name", "s", ARG_OUT }, - END_ARGS - } + { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_ssid, + NULL }, - { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - { "properties", "a{sv}", ARG_OUT }, - END_ARGS - } + { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_bssid, + NULL }, - { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - END_ARGS - } + { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q", + wpas_dbus_getter_p2p_group_frequency, + NULL }, - { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - END_ARGS - } + { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", + wpas_dbus_getter_p2p_group_passphrase, + NULL }, - { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "properties", "a{sv}", ARG_OUT }, - END_ARGS - } + { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_psk, + NULL }, -#ifdef CONFIG_WPS - { "Event", WPAS_DBUS_NEW_IFACE_WPS, - { - { "name", "s", ARG_OUT }, - { "args", "a{sv}", ARG_OUT }, - END_ARGS - } + { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay", + wpas_dbus_getter_p2p_group_vendor_ext, + wpas_dbus_setter_p2p_group_vendor_ext }, - { "Credentials", WPAS_DBUS_NEW_IFACE_WPS, + { NULL, NULL, NULL, NULL, NULL } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = { + { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP, { - { "credentials", "a{sv}", ARG_OUT }, + { "peer", "o", ARG_OUT }, END_ARGS } }, - { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS, + { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP, { - { "properties", "a{sv}", ARG_OUT }, + { "peer", "o", ARG_OUT }, END_ARGS } }, -#endif /* CONFIG_WPS */ { NULL, NULL, { END_ARGS } } }; +/** + * wpas_dbus_register_p2p_group - Register a p2p group object with dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: SSID struct + * Returns: 0 on success, -1 on failure + * + * Registers p2p group representing object with dbus + */ +void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; -int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (wpa_s->dbus_groupobj_path) { + wpa_printf(MSG_INFO, "%s: Group object '%s' already exists", + __func__, wpa_s->dbus_groupobj_path); + return; + } + + if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0) + return; + + wpa_s->dbus_groupobj_path = os_strdup(group_obj_path); + if (wpa_s->dbus_groupobj_path == NULL) + return; + + wpa_printf(MSG_INFO, "dbus: Register group object '%s'", + group_obj_path); + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + wpas_dbus_register(obj_desc, wpa_s, NULL, NULL, + wpas_dbus_p2p_group_properties, + wpas_dbus_p2p_group_signals); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + return; + +err: + if (wpa_s->dbus_groupobj_path) { + os_free(wpa_s->dbus_groupobj_path); + wpa_s->dbus_groupobj_path = NULL; + } + + free_dbus_object_desc(obj_desc); +} + +/** + * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: network name of the p2p group started + */ +void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) { + struct wpas_dbus_priv *ctrl_iface; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) { + wpa_printf(MSG_DEBUG, + "%s: Group object '%s' already unregistered", + __func__, wpa_s->dbus_groupobj_path); + return; + } + + wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'", + wpa_s->dbus_groupobj_path); + + wpa_dbus_unregister_object_per_iface(ctrl_iface, + wpa_s->dbus_groupobj_path); + os_free(wpa_s->dbus_groupobj_path); + wpa_s->dbus_groupobj_path = NULL; +} + +static const struct wpa_dbus_property_desc +wpas_dbus_p2p_groupmember_properties[] = { + { NULL, NULL, NULL, NULL, NULL } +}; + +/** + * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember + * object with dbus + * @wpa_s: wpa_supplicant interface structure + * @p2p_if_addr: i/f addr of the device joining this group + * + * Registers p2p groupmember representing object with dbus + */ +void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr) +{ + struct wpas_dbus_priv *ctrl_iface; struct wpa_dbus_object_desc *obj_desc = NULL; - struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; - int next; + struct groupmember_handler_args *arg; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) - return 0; + return; - /* Create and set the interface's object path */ - wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); - if (wpa_s->dbus_new_path == NULL) - return -1; - next = ctrl_iface->next_objid++; - os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX, - WPAS_DBUS_NEW_PATH_INTERFACES "/%u", - next); + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr)); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { @@ -1513,50 +3320,198 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) goto err; } - wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods, - wpas_dbus_interface_properties, - wpas_dbus_interface_signals); + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct groupmember_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for method"); + goto err; + } - wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'", - wpa_s->dbus_new_path); - if (wpa_dbus_register_object_per_iface(ctrl_iface, - wpa_s->dbus_new_path, + arg->wpa_s = wpa_s; + os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN); + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_p2p_groupmember_properties, NULL); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path, wpa_s->ifname, obj_desc)) goto err; - wpas_dbus_signal_interface_added(wpa_s); + wpa_printf(MSG_INFO, + "dbus: Registered group member object '%s' successfully", + groupmember_obj_path); + return; + +err: + free_dbus_object_desc(obj_desc); +} + +/** + * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember + * object with dbus + * @wpa_s: wpa_supplicant interface structure + * @p2p_if_addr: i/f addr of the device joining this group + * + * Unregisters p2p groupmember representing object with dbus + */ +void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr) +{ + struct wpas_dbus_priv *ctrl_iface; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr)); + + wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path); +} + + +static const struct wpa_dbus_property_desc + wpas_dbus_persistent_group_properties[] = { + { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}", + wpas_dbus_getter_persistent_group_properties, + wpas_dbus_setter_persistent_group_properties + }, + { NULL, NULL, NULL, NULL, NULL } +}; + +/* No signals intended for persistent group objects */ + +/** + * wpas_dbus_register_persistent_group - Register a configured(saved) + * persistent group with dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: persistent group (still represented as a network within wpa) + * configuration data + * Returns: 0 on success, -1 on failure + * + * Registers a persistent group representing object with dbus. + */ +int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + struct network_handler_args *arg; + char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + + /* Make sure ssid is a persistent group */ + if (ssid->disabled != 2 && !ssid->p2p_persistent_group) + return -1; /* should we return w/o complaining? */ + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + /* + * Intentionally not coming up with different numbering scheme + * for persistent groups. + */ + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", + wpa_s->dbus_new_path, ssid->id); + + wpa_printf(MSG_DEBUG, "dbus: Register persistent group object '%s'", + pgrp_obj_path); + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to create " + "object description"); + goto err; + } + + /* + * Reusing the same context structure as that for networks + * since these are represented using same data structure. + */ + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct network_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "dbus: Not enough memory to create " + "arguments for method"); + goto err; + } + + arg->wpa_s = wpa_s; + arg->ssid = ssid; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_persistent_group_properties, + NULL); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, pgrp_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + wpas_dbus_signal_persistent_group_added(wpa_s, ssid->id); return 0; err: - os_free(wpa_s->dbus_new_path); - wpa_s->dbus_new_path = NULL; free_dbus_object_desc(obj_desc); return -1; } -int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) +/** + * wpas_dbus_unregister_persistent_group - Unregister a persistent_group + * from dbus + * @wpa_s: wpa_supplicant interface structure + * @nid: network id + * Returns: 0 on success, -1 on failure + * + * Unregisters persistent group representing object from dbus + * + * NOTE: There is a slight issue with the semantics here. While the + * implementation simply means the persistent group is unloaded from memory, + * it should not get interpreted as the group is actually being erased/removed + * from persistent storage as well. + */ +int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, + int nid) { struct wpas_dbus_priv *ctrl_iface; + char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) + if (wpa_s == NULL || wpa_s->global == NULL || + wpa_s->dbus_new_path == NULL) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; - wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", - wpa_s->dbus_new_path); - if (wpa_dbus_unregister_object_per_iface(ctrl_iface, - wpa_s->dbus_new_path)) - return -1; + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", + wpa_s->dbus_new_path, nid); - wpas_dbus_signal_interface_removed(wpa_s); + wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'", + pgrp_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path); - os_free(wpa_s->dbus_new_path); - wpa_s->dbus_new_path = NULL; + if (!ret) + wpas_dbus_signal_persistent_group_removed(wpa_s, nid); - return 0; + return ret; } + +#endif /* CONFIG_P2P */ diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 80ea98c..93ce722 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -16,13 +16,15 @@ #ifndef CTRL_IFACE_DBUS_NEW_H #define CTRL_IFACE_DBUS_NEW_H +#include "common/defs.h" +#include "p2p/p2p.h" + struct wpa_global; struct wpa_supplicant; struct wpa_ssid; struct wps_event_m2d; struct wps_event_fail; struct wps_credential; -enum wpa_states; enum wpas_dbus_prop { WPAS_DBUS_PROP_AP_SCAN, @@ -30,6 +32,8 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_STATE, WPAS_DBUS_PROP_CURRENT_BSS, WPAS_DBUS_PROP_CURRENT_NETWORK, + WPAS_DBUS_PROP_CURRENT_AUTH_MODE, + WPAS_DBUS_PROP_BSSS, }; enum wpas_dbus_bss_prop { @@ -59,6 +63,29 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_NEW_BSSIDS_PART "BSSs" #define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS" +#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \ + WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice" + +/* + * Groups correspond to P2P groups where this device is a GO (owner) + */ +#define WPAS_DBUS_NEW_P2P_GROUPS_PART "Groups" +#define WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group" + +/* + * Different dbus object for persistent groups so they do not get confused + * with regular (configured) network objects. + */ +#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups" +#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \ + WPAS_DBUS_NEW_INTERFACE ".PersistentGroup" + +#define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers" +#define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer" + +#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "Members" +#define WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \ + WPAS_DBUS_NEW_INTERFACE ".GroupMember" /* Errors */ #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \ @@ -76,6 +103,13 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \ WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown" +#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \ + WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable" +#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \ + WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported" +#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \ + WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError" + #define WPAS_DBUS_ERROR_BLOB_EXISTS \ WPAS_DBUS_NEW_INTERFACE ".BlobExists" #define WPAS_DBUS_ERROR_BLOB_UNKNOWN \ @@ -97,6 +131,10 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id); +void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, + const char *default_text); void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success); void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, const struct wps_credential *cred); @@ -120,6 +158,59 @@ void wpas_dbus_signal_debug_level_changed(struct wpa_global *global); void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global); void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global); +int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr); +void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); +int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); +void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); +void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, + const char *role); +void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin); +void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id); +void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + int client, int network_id); +void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res); +void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid); +int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, + int nid); +void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid); +void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr); +void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr); +void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *member); +void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len); +void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); +void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, + const u8 *member); +void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); +void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, + int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert); + #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) @@ -155,6 +246,12 @@ static inline void wpas_dbus_signal_network_selected( { } +static inline void wpas_dbus_signal_network_request( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, const char *default_txt) +{ +} + static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) { @@ -229,6 +326,147 @@ static inline void wpas_dbus_signal_debug_show_keys_changed( { } +static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + return 0; +} + +static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + return 0; +} + +static inline void +wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, + const char *role) +{ +} + +static inline void +wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin) +{ +} + +static inline void wpas_dbus_signal_p2p_go_neg_req( + struct wpa_supplicant *wpa_s, + const u8 *src, + u16 dev_passwd_id) +{ +} + +static inline void +wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + int client, int network_id) +{ +} + +static inline void +wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +} + +static inline int wpas_dbus_register_persistent_group( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + return 0; +} + +static inline int wpas_dbus_unregister_persistent_group( + struct wpa_supplicant *wpa_s, int nid) +{ + return 0; +} + +static inline void +wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res) +{ +} + +static inline void +wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) +{ +} + +static inline void wpas_dbus_signal_p2p_invitation_result( + struct wpa_supplicant *wpa_s, int status, + const u8 *bssid) +{ +} + +static inline void +wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr) +{ +} + +static inline void +wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq, + const u8 *sa, u8 dialog_token, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ +} + +static inline void +wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ +} + +static inline void +wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr) +{ +} + +static inline void +wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, + const u8 *member) +{ +} + +static inline void +wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ +} + +static inline void +wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ +} + +static inline void +wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *member) +{ +} + +static inline void +wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ +} + +static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, + int depth, + const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index e2b5e50..b662fa4 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -28,6 +28,7 @@ #include "../wpas_glue.h" #include "../bss.h" #include "../scan.h" +#include "../ctrl_iface.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" @@ -38,75 +39,11 @@ extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; static const char *debug_strings[] = { - "msgdump", "debug", "info", "warning", "error", NULL + "excessive", "msgdump", "debug", "info", "warning", "error", NULL }; /** - * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts - * @path: The dbus object path - * @network: (out) the configured network this object path refers to, if any - * @bssid: (out) the scanned bssid this object path refers to, if any - * Returns: The object path of the network interface this path refers to - * - * For a given object path, decomposes the object path into object id, network, - * and BSSID parts, if those parts exist. - */ -static char * wpas_dbus_new_decompose_object_path(const char *path, - char **network, - char **bssid) -{ - const unsigned int dev_path_prefix_len = - strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); - char *obj_path_only; - char *next_sep; - - /* Be a bit paranoid about path */ - if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", - dev_path_prefix_len)) - return NULL; - - /* Ensure there's something at the end of the path */ - if ((path + dev_path_prefix_len)[0] == '\0') - return NULL; - - obj_path_only = os_strdup(path); - if (obj_path_only == NULL) - return NULL; - - next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); - if (next_sep != NULL) { - const char *net_part = os_strstr( - next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/"); - const char *bssid_part = os_strstr( - next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); - - if (network && net_part) { - /* Deal with a request for a configured network */ - const char *net_name = net_part + - os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/"); - *network = NULL; - if (os_strlen(net_name)) - *network = os_strdup(net_name); - } else if (bssid && bssid_part) { - /* Deal with a request for a scanned BSSID */ - const char *bssid_name = bssid_part + - os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); - if (strlen(bssid_name)) - *bssid = os_strdup(bssid_name); - else - *bssid = NULL; - } - - /* Cut off interface object path before "/" */ - *next_sep = '\0'; - } - - return obj_path_only; -} - - -/** * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message * @message: Pointer to incoming dbus message this error refers to * @arg: Optional string appended to error message @@ -117,6 +54,20 @@ static char * wpas_dbus_new_decompose_object_path(const char *path, DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg) { + /* + * This function can be called as a result of a failure + * within internal getter calls, which will call this function + * with a NULL message parameter. However, dbus_message_new_error + * looks very unkindly (i.e, abort()) on a NULL message, so + * in this case, we should not call it. + */ + if (message == NULL) { + wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error " + "called with NULL message (arg=%s)", + arg ? arg : "N/A"); + return NULL; + } + return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR, arg); } @@ -213,36 +164,35 @@ static struct wpa_supplicant * get_iface_by_dbus_path( /** * set_network_properties - Set properties of a configured network - * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network * @iter: DBus message iterator containing dictionary of network * properties to set. - * Returns: NULL when succeed or DBus error on failure + * @error: On failure, an error describing the failure + * Returns: TRUE if the request succeeds, FALSE if it failed * * Sets network configuration with parameters given id DBus dictionary */ -static DBusMessage * set_network_properties(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - DBusMessageIter *iter) +dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + DBusMessageIter *iter, + DBusError *error) { - struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; - DBusMessage *reply = NULL; DBusMessageIter iter_dict; + char *value = NULL; - if (!wpa_dbus_dict_open_read(iter, &iter_dict)) - return wpas_dbus_error_invalid_args(message, NULL); + if (!wpa_dbus_dict_open_read(iter, &iter_dict, error)) + return FALSE; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - char *value = NULL; size_t size = 50; int ret; - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { - reply = wpas_dbus_error_invalid_args(message, NULL); - break; - } + + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + value = NULL; if (entry.type == DBUS_TYPE_ARRAY && entry.array_type == DBUS_TYPE_BYTE) { if (entry.array_len <= 0) @@ -311,71 +261,59 @@ static DBusMessage * set_network_properties(DBusMessage *message, os_free(value); wpa_dbus_dict_entry_clear(&entry); - continue; - - error: - os_free(value); - reply = wpas_dbus_error_invalid_args(message, entry.key); - wpa_dbus_dict_entry_clear(&entry); - break; } - return reply; + return TRUE; + +error: + os_free(value); + wpa_dbus_dict_entry_clear(&entry); + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; } /** * wpas_dbus_simple_property_getter - Get basic type property - * @message: Pointer to incoming dbus message + * @iter: Message iter to use when appending arguments * @type: DBus type of property (must be basic type) * @val: pointer to place holding property value - * Returns: The DBus message containing response for Properties.Get call - * or DBus error message if error occurred. + * @error: On failure an error describing the failure + * Returns: TRUE if the request was successful, FALSE if it failed * * Generic getter for basic type properties. Type is required to be basic. */ -DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message, - const int type, const void *val) +dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter, + const int type, + const void *val, + DBusError *error) { - DBusMessage *reply = NULL; - DBusMessageIter iter, variant_iter; + DBusMessageIter variant_iter; if (!dbus_type_is_basic(type)) { - wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:" - " given type is not basic"); - return wpas_dbus_error_unknown_error(message, NULL); + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: given type is not basic", __func__); + return FALSE; } - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); - else - reply = dbus_message_new_method_return(message); - - if (reply != NULL) { - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container( - &iter, DBUS_TYPE_VARIANT, - wpa_dbus_type_as_string(type), &variant_iter) || - !dbus_message_iter_append_basic(&variant_iter, type, - val) || - !dbus_message_iter_close_container(&iter, &variant_iter)) { - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_property_getter: out of " - "memory to put property value into " - "message"); - dbus_message_unref(reply); - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); - } - } else { - wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:" - " out of memory to return property value"); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - } + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + wpa_dbus_type_as_string(type), + &variant_iter)) + goto error; - return reply; + if (!dbus_message_iter_append_basic(&variant_iter, type, val)) + goto error; + + if (!dbus_message_iter_close_container(iter, &variant_iter)) + goto error; + + return TRUE; + +error: + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: error constructing reply", __func__); + return FALSE; } @@ -384,102 +322,79 @@ DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message, * @message: Pointer to incoming dbus message * @type: DBus type of property (must be basic type) * @val: pointer to place where value being set will be stored - * Returns: NULL or DBus error message if error occurred. + * Returns: TRUE if the request was successful, FALSE if it failed * * Generic setter for basic type properties. Type is required to be basic. */ -DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message, - const int type, void *val) +dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter, + DBusError *error, + const int type, void *val) { - DBusMessageIter iter, variant_iter; + DBusMessageIter variant_iter; if (!dbus_type_is_basic(type)) { - wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:" - " given type is not basic"); - return wpas_dbus_error_unknown_error(message, NULL); - } - - if (!dbus_message_iter_init(message, &iter)) { - wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:" - " out of memory to return scanning state"); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: given type is not basic", __func__); + return FALSE; } - /* omit first and second argument and get value from third */ - dbus_message_iter_next(&iter); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &variant_iter); - + /* Look at the new value */ + dbus_message_iter_recurse(iter, &variant_iter); if (dbus_message_iter_get_arg_type(&variant_iter) != type) { - wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:" - " wrong property type"); - return wpas_dbus_error_invalid_args(message, - "wrong property type"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "wrong property type"); + return FALSE; } dbus_message_iter_get_basic(&variant_iter, val); - return NULL; + return TRUE; } /** * wpas_dbus_simple_array_property_getter - Get array type property - * @message: Pointer to incoming dbus message + * @iter: Pointer to incoming dbus message iterator * @type: DBus type of property array elements (must be basic type) * @array: pointer to array of elements to put into response message * @array_len: length of above array - * Returns: The DBus message containing response for Properties.Get call - * or DBus error message if error occurred. + * @error: a pointer to an error to fill on failure + * Returns: TRUE if the request succeeded, FALSE if it failed * * Generic getter for array type properties. Array elements type is * required to be basic. */ -DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, - const int type, - const void *array, - size_t array_len) +dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, + const int type, + const void *array, + size_t array_len, + DBusError *error) { - DBusMessage *reply = NULL; - DBusMessageIter iter, variant_iter, array_iter; + DBusMessageIter variant_iter, array_iter; char type_str[] = "a?"; /* ? will be replaced with subtype letter; */ const char *sub_type_str; size_t element_size, i; if (!dbus_type_is_basic(type)) { - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_array_property_getter: given " - "type is not basic"); - return wpas_dbus_error_unknown_error(message, NULL); + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: given type is not basic", __func__); + return FALSE; } sub_type_str = wpa_dbus_type_as_string(type); type_str[1] = sub_type_str[0]; - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); - else - reply = dbus_message_new_method_return(message); - if (reply == NULL) { - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_array_property_getter: out of " - "memory to create return message"); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + type_str, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 1", __func__); + return FALSE; } - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - type_str, &variant_iter) || - !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, sub_type_str, &array_iter)) { - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_array_property_getter: out of " - "memory to open container"); - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 2", __func__); + return FALSE; } switch(type) { @@ -507,11 +422,9 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, element_size = sizeof(char *); break; default: - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_array_property_getter: " - "fatal: unknown element type"); - element_size = 1; - break; + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: unknown element type %d", __func__, type); + return FALSE; } for (i = 0; i < array_len; i++) { @@ -519,17 +432,89 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, array + i * element_size); } - if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || - !dbus_message_iter_close_container(&iter, &variant_iter)) { - wpa_printf(MSG_ERROR, "dbus: " - "wpas_dbus_simple_array_property_getter: out of " - "memory to close container"); - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 3", __func__); + return FALSE; } - return reply; + if (!dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 4", __func__); + return FALSE; + } + + return TRUE; +} + + +/** + * wpas_dbus_simple_array_array_property_getter - Get array array type property + * @iter: Pointer to incoming dbus message iterator + * @type: DBus type of property array elements (must be basic type) + * @array: pointer to array of elements to put into response message + * @array_len: length of above array + * @error: a pointer to an error to fill on failure + * Returns: TRUE if the request succeeded, FALSE if it failed + * + * Generic getter for array type properties. Array elements type is + * required to be basic. + */ +dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, + const int type, + struct wpabuf **array, + size_t array_len, + DBusError *error) +{ + DBusMessageIter variant_iter, array_iter; + char type_str[] = "aa?"; + char inner_type_str[] = "a?"; + const char *sub_type_str; + size_t i; + + if (!dbus_type_is_basic(type)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: given type is not basic", __func__); + return FALSE; + } + + sub_type_str = wpa_dbus_type_as_string(type); + type_str[2] = sub_type_str[0]; + inner_type_str[1] = sub_type_str[0]; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + type_str, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 1", __func__); + return FALSE; + } + if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + inner_type_str, &array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 2", __func__); + return FALSE; + } + + for (i = 0; i < array_len; i++) { + wpa_dbus_dict_bin_array_add_element(&array_iter, + wpabuf_head(array[i]), + wpabuf_len(array[i])); + + } + + if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to close message 2", __func__); + return FALSE; + } + + if (!dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to close message 1", __func__); + return FALSE; + } + + return TRUE; } @@ -553,11 +538,12 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, struct wpa_dbus_dict_entry entry; char *driver = NULL; char *ifname = NULL; + char *confname = NULL; char *bridge_ifname = NULL; dbus_message_iter_init(message, &iter); - if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) @@ -574,6 +560,12 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); if (ifname == NULL) goto error; + } else if (!strcmp(entry.key, "ConfigFile") && + (entry.type == DBUS_TYPE_STRING)) { + confname = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (confname == NULL) + goto error; } else if (!strcmp(entry.key, "BridgeIfname") && (entry.type == DBUS_TYPE_STRING)) { bridge_ifname = os_strdup(entry.str_value); @@ -604,6 +596,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, os_memset(&iface, 0, sizeof(iface)); iface.driver = driver; iface.ifname = ifname; + iface.confname = confname; iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { @@ -706,79 +699,86 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, /** * wpas_dbus_getter_debug_level - Get debug level - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: DBus message with value of debug level + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "DebugLevel" property. */ -DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, + DBusError *error, + void *user_data) { const char *str; int idx = wpa_debug_level; + if (idx < 0) idx = 0; - if (idx > 4) - idx = 4; + if (idx > 5) + idx = 5; str = debug_strings[idx]; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &str); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &str, error); } /** * wpas_dbus_getter_debug_timestamp - Get debug timestamp - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: DBus message with value of debug timestamp + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "DebugTimestamp" property. */ -DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &wpa_debug_timestamp); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &wpa_debug_timestamp, error); } /** * wpas_dbus_getter_debug_show_keys - Get debug show keys - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: DBus message with value of debug show_keys + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "DebugShowKeys" property. */ -DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &wpa_debug_show_keys); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &wpa_debug_show_keys, error); } /** * wpas_dbus_setter_debug_level - Set debug level - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: %NULL or DBus error message + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "DebugLevel" property. */ -DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, + DBusError *error, void *user_data) { - DBusMessage *reply; + struct wpa_global *global = user_data; const char *str = NULL; int i, val = -1; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING, - &str); - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &str)) + return FALSE; for (i = 0; debug_strings[i]; i++) if (os_strcmp(debug_strings[i], str) == 0) { @@ -789,138 +789,142 @@ DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message, if (val < 0 || wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp, wpa_debug_show_keys)) { - dbus_message_unref(reply); - return wpas_dbus_error_invalid_args( - message, "Wrong debug level value"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug " + "level value"); + return FALSE; } - return NULL; + return TRUE; } /** * wpas_dbus_setter_debug_timestamp - Set debug timestamp - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: %NULL or DBus error message + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "DebugTimestamp" property. */ -DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply; + struct wpa_global *global = user_data; dbus_bool_t val; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, - &val); - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, + &val)) + return FALSE; wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0, wpa_debug_show_keys); - - return NULL; + return TRUE; } /** * wpas_dbus_setter_debug_show_keys - Set debug show keys - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: %NULL or DBus error message + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "DebugShowKeys" property. */ -DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply; + struct wpa_global *global = user_data; dbus_bool_t val; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, - &val); - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, + &val)) + return FALSE; wpa_supplicant_set_debug_params(global, wpa_debug_level, wpa_debug_timestamp, val ? 1 : 0); - - return NULL; + return TRUE; } /** * wpas_dbus_getter_interfaces - Request registered interfaces list - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: The object paths array containing registered interfaces - * objects paths or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Interfaces" property. Handles requests * by dbus clients to return list of registered interfaces objects * paths */ -DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message, - struct wpa_global *global) +dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_global *global = user_data; struct wpa_supplicant *wpa_s; const char **paths; unsigned int i = 0, num = 0; + dbus_bool_t success; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) num++; paths = os_zalloc(num * sizeof(char*)); if (!paths) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - paths[i] = wpa_s->dbus_new_path; + paths[i++] = wpa_s->dbus_new_path; - reply = wpas_dbus_simple_array_property_getter(message, - DBUS_TYPE_OBJECT_PATH, - paths, num); + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, num, error); os_free(paths); - return reply; + return success; } /** * wpas_dbus_getter_eap_methods - Request supported EAP methods list - * @message: Pointer to incoming dbus message - * @nothing: not used argument. may be NULL or anything else - * Returns: The object paths array containing supported EAP methods - * represented by strings or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "EapMethods" property. Handles requests * by dbus clients to return list of strings with supported EAP methods */ -DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing) +dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, + DBusError *error, void *user_data) { - DBusMessage *reply = NULL; char **eap_methods; size_t num_items = 0; + dbus_bool_t success; eap_methods = eap_get_names_as_string_array(&num_items); if (!eap_methods) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } - reply = wpas_dbus_simple_array_property_getter(message, - DBUS_TYPE_STRING, - eap_methods, num_items); + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_STRING, + eap_methods, + num_items, error); while (num_items) os_free(eap_methods[--num_items]); os_free(eap_methods); - return reply; + return success; } @@ -987,21 +991,34 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, dbus_message_iter_recurse(&array_iter, &sub_array_iter); dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); - if (len == 0) { - dbus_message_iter_next(&array_iter); - continue; - } - ssid = os_malloc(len); - if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. Cannot allocate memory for " - "SSID"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + if (len > MAX_SSID_LEN) { + wpa_printf(MSG_DEBUG, + "wpas_dbus_handler_scan[dbus]: " + "SSID too long (len=%d max_len=%d)", + len, MAX_SSID_LEN); + *reply = wpas_dbus_error_invalid_args( + message, "Invalid SSID: too long"); return -1; } - os_memcpy(ssid, val, len); + + if (len != 0) { + ssid = os_malloc(len); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, + "wpas_dbus_handler_scan[dbus]: " + "out of memory. Cannot allocate " + "memory for SSID"); + *reply = dbus_message_new_error( + message, DBUS_ERROR_NO_MEMORY, NULL); + return -1; + } + os_memcpy(ssid, val, len); + } else { + /* Allow zero-length SSIDs */ + ssid = NULL; + } + ssids[ssids_num].ssid = ssid; ssids[ssids_num].ssid_len = len; @@ -1260,14 +1277,16 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, "passive scan"); goto out; } else if (params.freqs && params.freqs[0]) { - /* wildcard ssid */ - params.num_ssids++; wpa_supplicant_trigger_scan(wpa_s, ¶ms); } else { wpa_s->scan_req = 2; wpa_supplicant_req_scan(wpa_s, 0, 0); } } else if (!os_strcmp(type, "active")) { + if (!params.num_ssids) { + /* Add wildcard ssid */ + params.num_ssids++; + } wpa_supplicant_trigger_scan(wpa_s, ¶ms); } else { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " @@ -1326,6 +1345,7 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, DBusMessageIter iter; struct wpa_ssid *ssid = NULL; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; + DBusError error; dbus_message_iter_init(message, &iter); @@ -1343,11 +1363,15 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, ssid->disabled = 1; wpa_config_set_network_defaults(ssid); - reply = set_network_properties(message, wpa_s, ssid, &iter); - if (reply) { + dbus_error_init(&error); + if (!set_network_properties(wpa_s, ssid, &iter, &error)) { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:" "control interface couldn't set network " "properties"); + reply = wpas_dbus_reply_new_from_error(message, &error, + DBUS_ERROR_INVALID_ARGS, + "Failed to add network"); + dbus_error_free(&error); goto err; } @@ -1403,7 +1427,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ - iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1444,6 +1468,42 @@ out: } +static void remove_network(void *arg, struct wpa_ssid *ssid) +{ + struct wpa_supplicant *wpa_s = arg; + + wpas_notify_network_removed(wpa_s, ssid); + + if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { + wpa_printf(MSG_ERROR, + "wpas_dbus_handler_remove_all_networks[dbus]: " + "error occurred when removing network %d", + ssid->id); + return; + } + + if (ssid == wpa_s->current_ssid) + wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); +} + + +/** + * wpas_dbus_handler_remove_all_networks - Remove all configured networks + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveAllNetworks" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_all_networks( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + /* NB: could check for failure and return an error */ + wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s); + return NULL; +} + + /** * wpas_dbus_handler_select_network - Attempt association with a network * @message: Pointer to incoming dbus message @@ -1466,7 +1526,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ - iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; @@ -1495,6 +1555,70 @@ out: /** + * wpas_dbus_handler_network_reply - Reply to a NetworkRequest signal + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "NetworkReply" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ +#ifdef IEEE8021X_EAPOL + DBusMessage *reply = NULL; + const char *op, *field, *value; + char *iface = NULL, *net_id = NULL; + int id; + struct wpa_ssid *ssid; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_STRING, &field, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID)) + return wpas_dbus_error_invalid_args(message, NULL); + + /* Extract the network ID and ensure the network */ + /* is actually a child of this interface */ + iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + id = strtoul(net_id, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args(message, net_id); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_error_network_unknown(message); + goto out; + } + + if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, + field, value) < 0) + reply = wpas_dbus_error_invalid_args(message, field); + else { + /* Tell EAP to retry immediately */ + eapol_sm_notify_ctrl_response(wpa_s->eapol); + } + +out: + os_free(iface); + os_free(net_id); + return reply; +#else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); + return wpas_dbus_error_unknown_error(message, "802.1X not included"); +#endif /* IEEE8021X_EAPOL */ +} + + +/** * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates) * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure @@ -1658,38 +1782,52 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, } - -/** - * wpas_dbus_getter_capabilities - Return interface capabilities +/* + * wpas_dbus_handler_flush_bss - Flush the BSS cache * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a dict of strings + * Returns: NULL * - * Getter for "Capabilities" property of an interface. + * Handler function for "FlushBSS" method call of network interface. */ -DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s) +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s) { - DBusMessage *reply = NULL; - struct wpa_driver_capa capa; - int res; - DBusMessageIter iter, iter_dict; - DBusMessageIter iter_dict_entry, iter_dict_val, iter_array, - variant_iter; - const char *scans[] = { "active", "passive", "ssid" }; - const char *modes[] = { "infrastructure", "ad-hoc", "ap" }; - int n = sizeof(modes) / sizeof(char *); + dbus_uint32_t age; + + dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age, + DBUS_TYPE_INVALID); - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + if (age == 0) + wpa_bss_flush(wpa_s); else - reply = dbus_message_new_method_return(message); - if (!reply) - goto nomem; + wpa_bss_flush_by_age(wpa_s, age); - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "a{sv}", &variant_iter)) + return NULL; +} + + +/** + * wpas_dbus_getter_capabilities - Return interface capabilities + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Capabilities" property of an interface. + */ +dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct wpa_driver_capa capa; + int res; + DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array, + variant_iter; + const char *scans[] = { "active", "passive", "ssid" }; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) goto nomem; if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) @@ -1949,41 +2087,78 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, goto nomem; /***** Modes */ - if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP)) - n--; /* exclude ap mode if it is not supported by the driver */ - if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n)) + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes", + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "infrastructure")) + goto nomem; + + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ad-hoc")) goto nomem; + if (res >= 0) { + if (capa.flags & (WPA_DRIVER_FLAGS_AP)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ap")) + goto nomem; + } + + if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "p2p")) + goto nomem; + } + } + + if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + /***** Modes end */ + + if (res >= 0) { + dbus_int32_t max_scan_ssid = capa.max_scan_ssids; + + if (!wpa_dbus_dict_append_int32(&iter_dict, "MaxScanSSID", + max_scan_ssid)) + goto nomem; + } + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) goto nomem; - if (!dbus_message_iter_close_container(&iter, &variant_iter)) + if (!dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; - return reply; + return TRUE; nomem: - if (reply) - dbus_message_unref(reply); - - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } /** * wpas_dbus_getter_state - Get interface state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a STRING representing the current - * interface state + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "State" property. */ -DBusMessage * wpas_dbus_getter_state(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s = user_data; const char *str_state; char *state_ls, *tmp; + dbus_bool_t success = FALSE; str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); @@ -1991,141 +2166,365 @@ DBusMessage * wpas_dbus_getter_state(DBusMessage *message, */ state_ls = tmp = os_strdup(str_state); if (!tmp) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } while (*tmp) { *tmp = tolower(*tmp); tmp++; } - reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &state_ls); + success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &state_ls, error); os_free(state_ls); - return reply; + return success; } /** * wpas_dbus_new_iface_get_scanning - Get interface scanning state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing whether the interface is scanning + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "scanning" property. */ -DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, + void *user_data) { + struct wpa_supplicant *wpa_s = user_data; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &scanning); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &scanning, error); } /** * wpas_dbus_getter_ap_scan - Control roaming mode - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A message containong value of ap_scan variable + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter function for "ApScan" property. */ -DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, + void *user_data) { + struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan = wpa_s->conf->ap_scan; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32, - &ap_scan); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &ap_scan, error); } /** * wpas_dbus_setter_ap_scan - Control roaming mode - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: NULL + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter function for "ApScan" property. */ -DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32, - &ap_scan); - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32, + &ap_scan)) + return FALSE; if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { - return wpas_dbus_error_invalid_args( - message, "ap_scan must equal 0, 1 or 2"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "ap_scan must be 0, 1, or 2"); + return FALSE; } - return NULL; + return TRUE; +} + + +/** + * wpas_dbus_getter_fast_reauth - Control fast + * reauthentication (TLS session resumption) + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "FastReauth" property. + */ +dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &fast_reauth, error); +} + + +/** + * wpas_dbus_setter_fast_reauth - Control fast + * reauthentication (TLS session resumption) + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "FastReauth" property. + */ +dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_bool_t fast_reauth; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, + &fast_reauth)) + return FALSE; + + wpa_s->conf->fast_reauth = fast_reauth; + return TRUE; +} + + +/** + * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "BSSExpireAge" property. + */ +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &expire_age, error); +} + + +/** + * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "BSSExpireAge" property. + */ +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_age; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32, + &expire_age)) + return FALSE; + + if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "BSSExpireAge must be >= 10"); + return FALSE; + } + return TRUE; +} + + +/** + * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "BSSExpireCount" property. + */ +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &expire_count, error); +} + + +/** + * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "BSSExpireCount" property. + */ +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_count; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32, + &expire_count)) + return FALSE; + + if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "BSSExpireCount must be > 0"); + return FALSE; + } + return TRUE; +} + + +/** + * wpas_dbus_getter_country - Control country code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "Country" property. + */ +dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char country[3]; + char *str = country; + + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &str, error); +} + + +/** + * wpas_dbus_setter_country - Control country code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "Country" property. + */ +dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *country; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &country)) + return FALSE; + + if (!country[0] || !country[1]) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "invalid country code"); + return FALSE; + } + + if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) { + wpa_printf(MSG_DEBUG, "Failed to set country"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to set country code"); + return FALSE; + } + + wpa_s->conf->country[0] = country[0]; + wpa_s->conf->country[1] = country[1]; + return TRUE; } /** * wpas_dbus_getter_ifname - Get interface name - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a name of network interface - * associated with with wpa_s + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Ifname" property. */ -DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, + void *user_data) { + struct wpa_supplicant *wpa_s = user_data; const char *ifname = wpa_s->ifname; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &ifname); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &ifname, error); } /** * wpas_dbus_getter_driver - Get interface name - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a name of network interface - * driver associated with with wpa_s + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Driver" property. */ -DBusMessage * wpas_dbus_getter_driver(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, + void *user_data) { + struct wpa_supplicant *wpa_s = user_data; const char *driver; if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) { wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: " "wpa_s has no driver set"); - return wpas_dbus_error_unknown_error(message, NULL); + dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set", + __func__); + return FALSE; } driver = wpa_s->driver->name; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &driver); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &driver, error); } /** * wpas_dbus_getter_current_bss - Get current bss object path - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a DBus object path to - * current BSS + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "CurrentBSS" property. */ -DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply; + struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; if (wpa_s->current_bss) @@ -2135,27 +2534,25 @@ DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message, else os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); - reply = wpas_dbus_simple_property_getter(message, - DBUS_TYPE_OBJECT_PATH, - &bss_obj_path); - - return reply; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &bss_obj_path, error); } /** * wpas_dbus_getter_current_network - Get current network object path - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a DBus object path to - * current network + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "CurrentNetwork" property. */ -DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply; + struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; if (wpa_s->current_ssid) @@ -2165,70 +2562,100 @@ DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message, else os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); - reply = wpas_dbus_simple_property_getter(message, - DBUS_TYPE_OBJECT_PATH, - &net_obj_path); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &net_obj_path, error); +} - return reply; + +/** + * wpas_dbus_getter_current_auth_mode - Get current authentication type + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "CurrentAuthMode" property. + */ +dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *eap_mode; + const char *auth_mode; + char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX]; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + auth_mode = "INACTIVE"; + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eap_mode = wpa_supplicant_get_eap_mode(wpa_s); + os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX, + "EAP-%s", eap_mode); + auth_mode = eap_mode_buf; + + } else { + auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt, + wpa_s->current_ssid->proto); + } + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &auth_mode, error); } /** * wpas_dbus_getter_bridge_ifname - Get interface name - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a name of bridge network - * interface associated with with wpa_s + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "BridgeIfname" property. */ -DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - const char *bridge_ifname = NULL; + struct wpa_supplicant *wpa_s = user_data; + const char *bridge_ifname; - bridge_ifname = wpa_s->bridge_ifname; - if (bridge_ifname == NULL) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: " - "wpa_s has no bridge interface name set"); - return wpas_dbus_error_unknown_error(message, NULL); - } - - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &bridge_ifname); + bridge_ifname = wpa_s->bridge_ifname ? wpa_s->bridge_ifname : ""; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &bridge_ifname, error); } /** * wpas_dbus_getter_bsss - Get array of BSSs objects - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing an array of all known BSS objects - * dbus paths + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "BSSs" property. */ -DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s = user_data; struct wpa_bss *bss; char **paths; unsigned int i = 0; + dbus_bool_t success = FALSE; paths = os_zalloc(wpa_s->num_bss * sizeof(char *)); if (!paths) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } /* Loop through scan results and append each result's object path */ dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (paths[i] == NULL) { - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); goto out; } /* Construct the object path for this BSS. */ @@ -2237,57 +2664,62 @@ DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, wpa_s->dbus_new_path, bss->id); } - reply = wpas_dbus_simple_array_property_getter(message, - DBUS_TYPE_OBJECT_PATH, - paths, wpa_s->num_bss); + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, wpa_s->num_bss, + error); out: while (i) os_free(paths[--i]); os_free(paths); - return reply; + return success; } /** * wpas_dbus_getter_networks - Get array of networks objects - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing an array of all configured - * networks dbus object paths. + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Networks" property. */ -DBusMessage * wpas_dbus_getter_networks(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; char **paths; unsigned int i = 0, num = 0; + dbus_bool_t success = FALSE; if (wpa_s->conf == NULL) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: " - "An error occurred getting networks list."); - return wpas_dbus_error_unknown_error(message, NULL); + wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting " + "networks list.", __func__); + dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error " + "occurred getting the networks list", __func__); + return FALSE; } for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) - num++; + if (!network_is_persistent_group(ssid)) + num++; paths = os_zalloc(num * sizeof(char *)); if (!paths) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } /* Loop through configured networks and append object path of each */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (network_is_persistent_group(ssid)) + continue; paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (paths[i] == NULL) { - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); goto out; } @@ -2297,50 +2729,40 @@ DBusMessage * wpas_dbus_getter_networks(DBusMessage *message, wpa_s->dbus_new_path, ssid->id); } - reply = wpas_dbus_simple_array_property_getter(message, - DBUS_TYPE_OBJECT_PATH, - paths, num); + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, num, error); out: while (i) os_free(paths[--i]); os_free(paths); - return reply; + return success; } /** * wpas_dbus_getter_blobs - Get all blobs defined for this interface - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing a dictionary of pairs (blob_name, blob) + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Blobs" property. */ -DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; - DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter; + struct wpa_supplicant *wpa_s = user_data; + DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter; struct wpa_config_blob *blob; - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); - else - reply = dbus_message_new_method_return(message); - if (!reply) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter) || !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } blob = wpa_s->conf->blobs; @@ -2363,176 +2785,196 @@ DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message, &array_iter) || !dbus_message_iter_close_container(&dict_iter, &entry_iter)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + return FALSE; } blob = blob->next; } if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) || - !dbus_message_iter_close_container(&iter, &variant_iter)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + !dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } - return reply; + return TRUE; +} + + +static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, + DBusError *error, const char *func_name) +{ + struct wpa_bss *res = wpa_bss_get_id(args->wpa_s, args->id); + + if (!res) { + wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found", + func_name, args->id); + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: BSS %d not found", + func_name, args->id); + } + + return res; } /** * wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the bssid for the requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "BSSID" property. */ -DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; - return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, - res->bssid, ETH_ALEN); + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + res->bssid, ETH_ALEN, + error); } /** * wpas_dbus_getter_bss_ssid - Return the SSID of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the ssid for the requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "SSID" property. */ -DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; - return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, - res->ssid, - res->ssid_len); + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + res->ssid, res->ssid_len, + error); } /** * wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the privacy flag value of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Privacy" property. */ -DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, + DBusError *error, void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; dbus_bool_t privacy; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &privacy); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &privacy, error); } /** * wpas_dbus_getter_bss_mode - Return the mode of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the mode of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Mode" property. */ -DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; const char *mode; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; if (res->caps & IEEE80211_CAP_IBSS) mode = "ad-hoc"; else mode = "infrastructure"; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &mode); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &mode, error); } /** * wpas_dbus_getter_bss_level - Return the signal strength of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the signal strength of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Level" property. */ -DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, + DBusError *error, void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; + s16 level; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16, - &res->level); + level = (s16) res->level; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT16, + &level, error); } /** * wpas_dbus_getter_bss_frequency - Return the frequency of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the frequency of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Frequency" property. */ -DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, + DBusError *error, void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; + u16 freq; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: " - "no bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16, - &res->freq); + freq = (u16) res->freq; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, + &freq, error); } @@ -2544,72 +2986,64 @@ static int cmp_u8s_desc(const void *a, const void *b) /** * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing sorted array of bit rates + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Rates" property. */ -DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, + DBusError *error, void *user_data) { - DBusMessage *reply; - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; u8 *ie_rates = NULL; u32 *real_rates; int rates_num, i; + dbus_bool_t success = FALSE; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: " - "no bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; rates_num = wpa_bss_get_bit_rates(res, &ie_rates); if (rates_num < 0) - return NULL; + return FALSE; qsort(ie_rates, rates_num, 1, cmp_u8s_desc); real_rates = os_malloc(sizeof(u32) * rates_num); if (!real_rates) { os_free(ie_rates); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } for (i = 0; i < rates_num; i++) real_rates[i] = ie_rates[i] * 500000; - reply = wpas_dbus_simple_array_property_getter(message, - DBUS_TYPE_UINT32, - real_rates, rates_num); + success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_UINT32, + real_rates, rates_num, + error); os_free(ie_rates); os_free(real_rates); - return reply; + return success; } -static DBusMessage * wpas_dbus_get_bss_security_prop( - DBusMessage *message, struct wpa_ie_data *ie_data) +static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, + struct wpa_ie_data *ie_data, + DBusError *error) { - DBusMessage *reply; - DBusMessageIter iter, iter_dict, variant_iter; + DBusMessageIter iter_dict, variant_iter; const char *group; const char *pairwise[2]; /* max 2 pairwise ciphers is supported */ const char *key_mgmt[7]; /* max 7 key managements may be supported */ int n; - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); - else - reply = dbus_message_new_method_return(message); - if (!reply) - goto nomem; - - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter)) goto nomem; @@ -2690,152 +3124,152 @@ static DBusMessage * wpas_dbus_get_bss_security_prop( if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) goto nomem; - if (!dbus_message_iter_close_container(&iter, &variant_iter)) + if (!dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; - return reply; + return TRUE; nomem: - if (reply) - dbus_message_unref(reply); - - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } /** * wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the WPA options of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "WPA" property. */ -DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; struct wpa_ie_data wpa_data; const u8 *ie; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; os_memset(&wpa_data, 0, sizeof(wpa_data)); ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); if (ie) { - if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) - return wpas_dbus_error_unknown_error(message, - "invalid WPA IE"); + if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to parse WPA IE"); + return FALSE; + } } - return wpas_dbus_get_bss_security_prop(message, &wpa_data); + return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); } /** * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing the RSN options of requested bss + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "RSN" property. */ -DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; struct wpa_ie_data wpa_data; const u8 *ie; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; os_memset(&wpa_data, 0, sizeof(wpa_data)); ie = wpa_bss_get_ie(res, WLAN_EID_RSN); if (ie) { - if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) - return wpas_dbus_error_unknown_error(message, - "invalid RSN IE"); + if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to parse RSN IE"); + return FALSE; + } } - return wpas_dbus_get_bss_security_prop(message, &wpa_data); + return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); } /** * wpas_dbus_getter_bss_ies - Return all IEs of a BSS - * @message: Pointer to incoming dbus message - * @bss: a pair of interface describing structure and bss's id - * Returns: a dbus message containing IEs byte array + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "IEs" property. */ -DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message, - struct bss_handler_args *bss) +dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, + void *user_data) { - struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id); + struct bss_handler_args *args = user_data; + struct wpa_bss *res; - if (!res) { - wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no " - "bss with id %d found", bss->id); - return NULL; - } + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; - return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE, - res + 1, res->ie_len); + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + res + 1, res->ie_len, + error); } /** * wpas_dbus_getter_enabled - Check whether network is enabled or disabled - * @message: Pointer to incoming dbus message - * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface - * and wpa_ssid structure for a configured network - * Returns: DBus message with boolean indicating state of configured network - * or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "enabled" property of a configured network. */ -DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message, - struct network_handler_args *net) +dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, + void *user_data) { + struct network_handler_args *net = user_data; dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE; - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &enabled); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &enabled, error); } /** * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled - * @message: Pointer to incoming dbus message - * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface - * and wpa_ssid structure for a configured network - * Returns: NULL indicating success or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "Enabled" property of a configured network. */ -DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message, - struct network_handler_args *net) +dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; - + struct network_handler_args *net = user_data; struct wpa_supplicant *wpa_s; struct wpa_ssid *ssid; - dbus_bool_t enable; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, - &enable); - - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, + &enable)) + return FALSE; wpa_s = net->wpa_s; ssid = net->ssid; @@ -2845,48 +3279,38 @@ DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message, else wpa_supplicant_disable_network(wpa_s, ssid); - return NULL; + return TRUE; } /** * wpas_dbus_getter_network_properties - Get options for a configured network - * @message: Pointer to incoming dbus message - * @net: wpa_supplicant structure for a network interface and - * wpa_ssid structure for a configured network - * Returns: DBus message with network properties or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Getter for "Properties" property of a configured network. */ -DBusMessage * wpas_dbus_getter_network_properties( - DBusMessage *message, struct network_handler_args *net) +dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; - DBusMessageIter iter, variant_iter, dict_iter; + struct network_handler_args *net = user_data; + DBusMessageIter variant_iter, dict_iter; char **iterator; - char **props = wpa_config_get_all(net->ssid, 0); - if (!props) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + char **props = wpa_config_get_all(net->ssid, 1); + dbus_bool_t success = FALSE; - if (message == NULL) - reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); - else - reply = dbus_message_new_method_return(message); - if (!reply) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; + if (!props) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } - dbus_message_iter_init_append(reply, &iter); - - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "a{sv}", &variant_iter) || + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", + &variant_iter) || !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); goto out; } @@ -2894,10 +3318,8 @@ DBusMessage * wpas_dbus_getter_network_properties( while (*iterator) { if (!wpa_dbus_dict_append_string(&dict_iter, *iterator, *(iterator + 1))) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); goto out; } iterator += 2; @@ -2905,13 +3327,13 @@ DBusMessage * wpas_dbus_getter_network_properties( if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || - !dbus_message_iter_close_container(&iter, &variant_iter)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + !dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); goto out; } + success = TRUE; + out: iterator = props; while (*iterator) { @@ -2919,39 +3341,27 @@ out: iterator++; } os_free(props); - return reply; + return success; } /** * wpas_dbus_setter_network_properties - Set options for a configured network - * @message: Pointer to incoming dbus message - * @net: wpa_supplicant structure for a network interface and - * wpa_ssid structure for a configured network - * Returns: NULL indicating success or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "Properties" property of a configured network. */ -DBusMessage * wpas_dbus_setter_network_properties( - DBusMessage *message, struct network_handler_args *net) +dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data) { + struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; + DBusMessageIter variant_iter; - DBusMessage *reply = NULL; - DBusMessageIter iter, variant_iter; - - dbus_message_iter_init(message, &iter); - - dbus_message_iter_next(&iter); - dbus_message_iter_next(&iter); - - dbus_message_iter_recurse(&iter, &variant_iter); - - reply = set_network_properties(message, net->wpa_s, ssid, - &variant_iter); - if (reply) - wpa_printf(MSG_DEBUG, "dbus control interface couldn't set " - "network properties"); - - return reply; + dbus_message_iter_recurse(iter, &variant_iter); + return set_network_properties(net->wpa_s, ssid, &variant_iter, error); } diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 3cdf9cb..b46658f 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -26,17 +26,26 @@ struct bss_handler_args { unsigned int id; }; -DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message, - const int type, - const void *val); - -DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message, - const int type, void *val); - -DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message, - const int type, - const void *array, - size_t array_len); +dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter, + const int type, + const void *val, + DBusError *error); + +dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter, + DBusError *error, + const int type, void *val); + +dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, + const int type, + const void *array, + size_t array_len, + DBusError *error); + +dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, + const int type, + struct wpabuf **array, + size_t array_len, + DBusError *error); DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, struct wpa_global *global); @@ -47,29 +56,35 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, struct wpa_global *global); -DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message, - struct wpa_global *global); +dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, - void *nothing); +dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, + DBusError *error, void *user_data); DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -77,15 +92,26 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s); +dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + DBusMessageIter *iter, + DBusError *error); + DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_remove_all_networks( + DBusMessage *message, struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -95,98 +121,137 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, struct wpa_supplicant *wpa_s); -DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s); -DBusMessage * wpas_dbus_getter_state(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_driver(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_networks(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message, - struct wpa_supplicant *bss); +dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message, - struct bss_handler_args *bss); +dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message, - struct network_handler_args *net); +dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message, - struct network_handler_args *net); +dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_network_properties( - DBusMessage *message, struct network_handler_args *net); +dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_setter_network_properties( - DBusMessage *message, struct network_handler_args *net); +dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data); DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, struct wpa_supplicant *wpa_s); -DBusMessage * wpas_dbus_getter_process_credentials( - DBusMessage *message, struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_setter_process_credentials( - DBusMessage *message, struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_getter_credentials(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, + DBusError *error, + void *user_data); DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c new file mode 100644 index 0000000..aa05f58 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -0,0 +1,2456 @@ +/* + * WPA Supplicant / dbus-based control interface (P2P) + * + * 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 "utils/includes.h" +#include "common.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../wps_supplicant.h" +#include "../notify.h" +#include "dbus_new_helpers.h" +#include "dbus_new.h" +#include "dbus_new_handlers.h" +#include "dbus_new_handlers_p2p.h" +#include "dbus_dict_helpers.h" +#include "p2p/p2p.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/wps_hostapd.h" + +#include "../p2p_supplicant.h" + +/** + * Parses out the mac address from the peer object path. + * @peer_path - object path of the form + * /fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons) + * @addr - out param must be of ETH_ALEN size + * Returns 0 if valid (including MAC), -1 otherwise + */ +static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN]) +{ + char *p; + + if (!peer_path) + return -1; + p = strrchr(peer_path, '/'); + if (!p) + return -1; + p++; + return hwaddr_compact_aton(p, addr); +} + + +/** + * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown + * error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid persistent group error. + */ +static DBusMessage * wpas_dbus_error_persistent_group_unknown( + DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such persistent group in " + "this P2P device."); +} + + +DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + DBusMessageIter iter; + DBusMessageIter iter_dict; + unsigned int timeout = 0; + enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL; + int num_req_dev_types = 0; + unsigned int i; + u8 *req_dev_types = NULL; + + dbus_message_iter_init(message, &iter); + entry.key = NULL; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "Timeout") && + (entry.type == DBUS_TYPE_INT32)) { + timeout = entry.uint32_value; + } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != WPAS_DBUS_TYPE_BINARRAY)) + goto error_clear; + + os_free(req_dev_types); + req_dev_types = + os_malloc(WPS_DEV_TYPE_LEN * entry.array_len); + if (!req_dev_types) + goto error_clear; + + for (i = 0; i < entry.array_len; i++) { + if (wpabuf_len(entry.binarray_value[i]) != + WPS_DEV_TYPE_LEN) + goto error_clear; + os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN, + wpabuf_head(entry.binarray_value[i]), + WPS_DEV_TYPE_LEN); + } + num_req_dev_types = entry.array_len; + } else if (!os_strcmp(entry.key, "DiscoveryType") && + (entry.type == DBUS_TYPE_STRING)) { + if (!os_strcmp(entry.str_value, "start_with_full")) + type = P2P_FIND_START_WITH_FULL; + else if (!os_strcmp(entry.str_value, "social")) + type = P2P_FIND_ONLY_SOCIAL; + else if (!os_strcmp(entry.str_value, "progressive")) + type = P2P_FIND_PROGRESSIVE; + else + goto error_clear; + } else + goto error_clear; + wpa_dbus_dict_entry_clear(&entry); + } + + wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, + NULL); + os_free(req_dev_types); + return reply; + +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + os_free(req_dev_types); + reply = wpas_dbus_error_invalid_args(message, entry.key); + return reply; +} + + +DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpas_p2p_stop_find(wpa_s); + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + char *peer_object_path = NULL; + u8 peer_addr[ETH_ALEN]; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &peer_object_path); + + if (parse_peer_object_path(peer_object_path, peer_addr) < 0) + return wpas_dbus_error_invalid_args(message, NULL); + + if (wpas_p2p_reject(wpa_s, peer_addr) < 0) + return wpas_dbus_error_unknown_error(message, + "Failed to call wpas_p2p_reject method."); + + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_int32_t timeout = 0; + + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout, + DBUS_TYPE_INVALID)) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + + if (wpas_p2p_listen(wpa_s, (unsigned int)timeout)) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_extendedlisten( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + unsigned int period = 0, interval = 0; + struct wpa_dbus_dict_entry entry; + DBusMessageIter iter; + DBusMessageIter iter_dict; + + dbus_message_iter_init(message, &iter); + entry.key = NULL; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "period") && + (entry.type == DBUS_TYPE_INT32)) + period = entry.uint32_value; + else if (!os_strcmp(entry.key, "interval") && + (entry.type == DBUS_TYPE_INT32)) + interval = entry.uint32_value; + else + goto error_clear; + wpa_dbus_dict_entry_clear(&entry); + } + + if (wpas_p2p_ext_listen(wpa_s, period, interval)) + return wpas_dbus_error_unknown_error( + message, "failed to initiate a p2p_ext_listen."); + + return NULL; + +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + return wpas_dbus_error_invalid_args(message, entry.key); +} + + +DBusMessage * wpas_dbus_handler_p2p_presence_request( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0; + struct wpa_dbus_dict_entry entry; + DBusMessageIter iter; + DBusMessageIter iter_dict; + + dbus_message_iter_init(message, &iter); + entry.key = NULL; + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "duration1") && + (entry.type == DBUS_TYPE_INT32)) + dur1 = entry.uint32_value; + else if (!os_strcmp(entry.key, "interval1") && + entry.type == DBUS_TYPE_INT32) + int1 = entry.uint32_value; + else if (!os_strcmp(entry.key, "duration2") && + entry.type == DBUS_TYPE_INT32) + dur2 = entry.uint32_value; + else if (!os_strcmp(entry.key, "interval2") && + entry.type == DBUS_TYPE_INT32) + int2 = entry.uint32_value; + else + goto error_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) + return wpas_dbus_error_unknown_error(message, + "Failed to invoke presence request."); + + return NULL; + +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + return wpas_dbus_error_invalid_args(message, entry.key); +} + + +DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *pg_object_path = NULL; + int persistent_group = 0; + int freq = 0; + char *iface = NULL; + char *net_id_str = NULL; + unsigned int group_id = 0; + struct wpa_ssid *ssid; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto inv_args; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto inv_args; + + if (!os_strcmp(entry.key, "persistent") && + (entry.type == DBUS_TYPE_BOOLEAN)) { + persistent_group = (entry.bool_value == TRUE) ? 1 : 0; + } else if (!os_strcmp(entry.key, "frequency") && + (entry.type == DBUS_TYPE_INT32)) { + freq = entry.int32_value; + if (freq <= 0) + goto inv_args_clear; + } else if (!os_strcmp(entry.key, "persistent_group_object") && + entry.type == DBUS_TYPE_OBJECT_PATH) + pg_object_path = os_strdup(entry.str_value); + else + goto inv_args_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (pg_object_path != NULL) { + /* + * A persistent group Object Path is defined meaning we want + * to re-invoke a persistent group. + */ + + iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1, + &net_id_str, NULL); + if (iface == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = + wpas_dbus_error_invalid_args(message, + pg_object_path); + goto out; + } + + group_id = strtoul(net_id_str, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args( + message, pg_object_path); + goto out; + } + + /* Get the SSID structure from the persistent group id */ + ssid = wpa_config_get_network(wpa_s->conf, group_id); + if (ssid == NULL || ssid->disabled != 2) + goto inv_args; + + if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq)) { + reply = wpas_dbus_error_unknown_error( + message, + "Failed to reinvoke a persistent group"); + goto out; + } + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq)) + goto inv_args; + +out: + os_free(pg_object_path); + os_free(net_id_str); + os_free(iface); + return reply; +inv_args_clear: + wpa_dbus_dict_entry_clear(&entry); +inv_args: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_p2p_disconnect(wpa_s)) + return wpas_dbus_error_unknown_error(message, + "failed to disconnect"); + + return NULL; +} + + +static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, + DBusMessage *message, + DBusMessage **out_reply, + DBusError *error) +{ + /* Return an error message or an error if P2P isn't available */ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) { + if (out_reply) { + *out_reply = dbus_message_new_error( + message, DBUS_ERROR_FAILED, + "P2P is not available for this interface"); + } + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "P2P is not available for this " + "interface"); + return FALSE; + } + return TRUE; +} + + +DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) + return reply; + + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->force_long_sd = 0; + p2p_flush(wpa_s->global->p2p); + + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + int persistent_group = 0; + int join = 0; + int authorize_only = 0; + int go_intent = -1; + int freq = 0; + u8 addr[ETH_ALEN]; + char *pin = NULL; + enum p2p_wps_method wps_method = WPS_NOT_READY; + int new_pin; + char *err_msg = NULL; + char *iface = NULL; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) + return reply; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto inv_args; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto inv_args; + + if (!os_strcmp(entry.key, "peer") && + (entry.type == DBUS_TYPE_OBJECT_PATH)) { + peer_object_path = os_strdup(entry.str_value); + } else if (!os_strcmp(entry.key, "persistent") && + (entry.type == DBUS_TYPE_BOOLEAN)) { + persistent_group = (entry.bool_value == TRUE) ? 1 : 0; + } else if (!os_strcmp(entry.key, "join") && + (entry.type == DBUS_TYPE_BOOLEAN)) { + join = (entry.bool_value == TRUE) ? 1 : 0; + } else if (!os_strcmp(entry.key, "authorize_only") && + (entry.type == DBUS_TYPE_BOOLEAN)) { + authorize_only = (entry.bool_value == TRUE) ? 1 : 0; + } else if (!os_strcmp(entry.key, "frequency") && + (entry.type == DBUS_TYPE_INT32)) { + freq = entry.int32_value; + if (freq <= 0) + goto inv_args_clear; + } else if (!os_strcmp(entry.key, "go_intent") && + (entry.type == DBUS_TYPE_INT32)) { + go_intent = entry.int32_value; + if ((go_intent < 0) || (go_intent > 15)) + goto inv_args_clear; + } else if (!os_strcmp(entry.key, "wps_method") && + (entry.type == DBUS_TYPE_STRING)) { + if (!os_strcmp(entry.str_value, "pbc")) + wps_method = WPS_PBC; + else if (!os_strcmp(entry.str_value, "pin")) + wps_method = WPS_PIN_DISPLAY; + else if (!os_strcmp(entry.str_value, "display")) + wps_method = WPS_PIN_DISPLAY; + else if (!os_strcmp(entry.str_value, "keypad")) + wps_method = WPS_PIN_KEYPAD; + else + goto inv_args_clear; + } else if (!os_strcmp(entry.key, "pin") && + (entry.type == DBUS_TYPE_STRING)) { + pin = os_strdup(entry.str_value); + } else + goto inv_args_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (!peer_object_path || (wps_method == WPS_NOT_READY) || + (parse_peer_object_path(peer_object_path, addr) < 0) || + !p2p_peer_known(wpa_s->global->p2p, addr)) + goto inv_args; + + /* + * Validate the wps_method specified and the pin value. + */ + if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD)) + goto inv_args; + + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, + persistent_group, join, authorize_only, + go_intent, freq); + + if (new_pin >= 0) { + char npin[9]; + char *generated_pin; + os_snprintf(npin, sizeof(npin), "%08d", new_pin); + generated_pin = npin; + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_STRING, + &generated_pin, DBUS_TYPE_INVALID); + } else { + switch (new_pin) { + case -2: + err_msg = "connect failed due to channel " + "unavailability."; + iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE; + break; + + case -3: + err_msg = "connect failed due to unsupported channel."; + iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED; + break; + + default: + err_msg = "connect failed due to unspecified error."; + iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR; + break; + } + + /* + * TODO: + * Do we need specialized errors corresponding to above + * error conditions as against just returning a different + * error message? + */ + reply = dbus_message_new_error(message, iface, err_msg); + } + +out: + os_free(peer_object_path); + os_free(pin); + return reply; +inv_args_clear: + wpa_dbus_dict_entry_clear(&entry); +inv_args: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + char *pg_object_path = NULL; + char *iface = NULL; + char *net_id_str = NULL; + u8 peer_addr[ETH_ALEN]; + unsigned int group_id = 0; + int persistent = 0; + struct wpa_ssid *ssid; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) + return reply; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto err; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto err; + + if (!os_strcmp(entry.key, "peer") && + (entry.type == DBUS_TYPE_OBJECT_PATH)) { + peer_object_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } else if (!os_strcmp(entry.key, "persistent_group_object") && + (entry.type == DBUS_TYPE_OBJECT_PATH)) { + pg_object_path = os_strdup(entry.str_value); + persistent = 1; + wpa_dbus_dict_entry_clear(&entry); + } else { + wpa_dbus_dict_entry_clear(&entry); + goto err; + } + } + + if (!peer_object_path || + (parse_peer_object_path(peer_object_path, peer_addr) < 0) || + !p2p_peer_known(wpa_s->global->p2p, peer_addr)) { + goto err; + } + + if (persistent) { + /* + * A group ID is defined meaning we want to re-invoke a + * persistent group + */ + + iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1, + &net_id_str, NULL); + if (iface == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, + pg_object_path); + goto out; + } + + group_id = strtoul(net_id_str, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args( + message, pg_object_path); + goto out; + } + + /* Get the SSID structure from the persistent group id */ + ssid = wpa_config_get_network(wpa_s->conf, group_id); + if (ssid == NULL || ssid->disabled != 2) + goto err; + + if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL) < 0) { + reply = wpas_dbus_error_unknown_error( + message, + "Failed to reinvoke a persistent group"); + goto out; + } + } else { + /* + * No group ID means propose to a peer to join my active group + */ + if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname, + peer_addr, NULL)) { + reply = wpas_dbus_error_unknown_error( + message, "Failed to join to an active group"); + goto out; + } + } + +out: + os_free(pg_object_path); + os_free(peer_object_path); + return reply; + +err: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + char *peer_object_path = NULL; + char *config_method = NULL; + u8 peer_addr[ETH_ALEN]; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &peer_object_path); + + if (parse_peer_object_path(peer_object_path, peer_addr) < 0) + return wpas_dbus_error_invalid_args(message, NULL); + + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &config_method); + + /* + * Validation checks on config_method are being duplicated here + * to be able to return invalid args reply since the error code + * from p2p module are not granular enough (yet). + */ + if (os_strcmp(config_method, "display") && + os_strcmp(config_method, "keypad") && + os_strcmp(config_method, "pbc") && + os_strcmp(config_method, "pushbutton")) + return wpas_dbus_error_invalid_args(message, NULL); + + if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0) < 0) + return wpas_dbus_error_unknown_error(message, + "Failed to send provision discovery request"); + + return NULL; +} + + +/* + * P2P Device property accessor methods. + */ + +dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + DBusMessageIter variant_iter, dict_iter; + DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val, + iter_secdev_dict_array; + const char *dev_name; + int num_vendor_extensions = 0; + int i; + const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + return FALSE; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) + goto err_no_mem; + + /* DeviceName */ + dev_name = wpa_s->conf->device_name; + if (dev_name && + !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name)) + goto err_no_mem; + + /* Primary device type */ + if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType", + (char *)wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN)) + goto err_no_mem; + + /* Secondary device types */ + if (wpa_s->conf->num_sec_device_types) { + if (!wpa_dbus_dict_begin_array(&dict_iter, + "SecondaryDeviceTypes", + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &iter_secdev_dict_entry, + &iter_secdev_dict_val, + &iter_secdev_dict_array)) + goto err_no_mem; + + for (i = 0; i < wpa_s->conf->num_sec_device_types; i++) + wpa_dbus_dict_bin_array_add_element( + &iter_secdev_dict_array, + wpa_s->conf->sec_device_type[i], + WPS_DEV_TYPE_LEN); + + if (!wpa_dbus_dict_end_array(&dict_iter, + &iter_secdev_dict_entry, + &iter_secdev_dict_val, + &iter_secdev_dict_array)) + goto err_no_mem; + } + + /* Vendor Extensions */ + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + vendor_ext[num_vendor_extensions++] = + wpa_s->conf->wps_vendor_ext[i]; + } + + if (num_vendor_extensions && + !wpa_dbus_dict_append_wpabuf_array(&dict_iter, + "VendorExtension", + vendor_ext, + num_vendor_extensions)) + goto err_no_mem; + + /* GO Intent */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent", + wpa_s->conf->p2p_go_intent)) + goto err_no_mem; + + /* Persistent Reconnect */ + if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect", + wpa_s->conf->persistent_reconnect)) + goto err_no_mem; + + /* Listen Reg Class */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass", + wpa_s->conf->p2p_listen_reg_class)) + goto err_no_mem; + + /* Listen Channel */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel", + wpa_s->conf->p2p_listen_channel)) + goto err_no_mem; + + /* Oper Reg Class */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass", + wpa_s->conf->p2p_oper_reg_class)) + goto err_no_mem; + + /* Oper Channel */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel", + wpa_s->conf->p2p_oper_channel)) + goto err_no_mem; + + /* SSID Postfix */ + if (wpa_s->conf->p2p_ssid_postfix && + !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix", + wpa_s->conf->p2p_ssid_postfix)) + goto err_no_mem; + + /* Intra Bss */ + if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss", + wpa_s->conf->p2p_intra_bss)) + goto err_no_mem; + + /* Group Idle */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle", + wpa_s->conf->p2p_group_idle)) + goto err_no_mem; + + /* Dissasociation low ack */ + if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack", + wpa_s->conf->disassoc_low_ack)) + goto err_no_mem; + + if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) + goto err_no_mem; + + return TRUE; + +err_no_mem: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; +} + + +dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + DBusMessageIter variant_iter, iter_dict; + struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING }; + unsigned int i; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + return FALSE; + + dbus_message_iter_recurse(iter, &variant_iter); + if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) + return FALSE; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; + } + + if (os_strcmp(entry.key, "DeviceName") == 0) { + char *devname; + + if (entry.type != DBUS_TYPE_STRING) + goto error; + + devname = os_strdup(entry.str_value); + if (devname == NULL) + goto err_no_mem_clear; + + os_free(wpa_s->conf->device_name); + wpa_s->conf->device_name = devname; + + wpa_s->conf->changed_parameters |= + CFG_CHANGED_DEVICE_NAME; + } else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE || + entry.array_len != WPS_DEV_TYPE_LEN) + goto error; + + os_memcpy(wpa_s->conf->device_type, + entry.bytearray_value, + WPS_DEV_TYPE_LEN); + wpa_s->conf->changed_parameters |= + CFG_CHANGED_DEVICE_TYPE; + } else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY || + entry.array_len > MAX_SEC_DEVICE_TYPES) + goto error; + + for (i = 0; i < entry.array_len; i++) + if (wpabuf_len(entry.binarray_value[i]) != + WPS_DEV_TYPE_LEN) + goto err_no_mem_clear; + for (i = 0; i < entry.array_len; i++) + os_memcpy(wpa_s->conf->sec_device_type[i], + wpabuf_head(entry.binarray_value[i]), + WPS_DEV_TYPE_LEN); + wpa_s->conf->num_sec_device_types = entry.array_len; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_SEC_DEVICE_TYPE; + } else if (os_strcmp(entry.key, "VendorExtension") == 0) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) || + (entry.array_len > P2P_MAX_WPS_VENDOR_EXT)) + goto error; + + wpa_s->conf->changed_parameters |= + CFG_CHANGED_VENDOR_EXTENSION; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(wpa_s->conf->wps_vendor_ext[i]); + if (i < entry.array_len) { + wpa_s->conf->wps_vendor_ext[i] = + entry.binarray_value[i]; + entry.binarray_value[i] = NULL; + } else + wpa_s->conf->wps_vendor_ext[i] = NULL; + } + } else if ((os_strcmp(entry.key, "GOIntent") == 0) && + (entry.type == DBUS_TYPE_UINT32) && + (entry.uint32_value <= 15)) + wpa_s->conf->p2p_go_intent = entry.uint32_value; + else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) && + (entry.type == DBUS_TYPE_BOOLEAN)) + wpa_s->conf->persistent_reconnect = entry.bool_value; + else if ((os_strcmp(entry.key, "ListenRegClass") == 0) && + (entry.type == DBUS_TYPE_UINT32)) { + wpa_s->conf->p2p_listen_reg_class = entry.uint32_value; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_LISTEN_CHANNEL; + } else if ((os_strcmp(entry.key, "ListenChannel") == 0) && + (entry.type == DBUS_TYPE_UINT32)) { + wpa_s->conf->p2p_listen_channel = entry.uint32_value; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_LISTEN_CHANNEL; + } else if ((os_strcmp(entry.key, "OperRegClass") == 0) && + (entry.type == DBUS_TYPE_UINT32)) { + wpa_s->conf->p2p_oper_reg_class = entry.uint32_value; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_OPER_CHANNEL; + } else if ((os_strcmp(entry.key, "OperChannel") == 0) && + (entry.type == DBUS_TYPE_UINT32)) { + wpa_s->conf->p2p_oper_channel = entry.uint32_value; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_OPER_CHANNEL; + } else if (os_strcmp(entry.key, "SsidPostfix") == 0) { + char *postfix; + + if (entry.type != DBUS_TYPE_STRING) + goto error; + + postfix = os_strdup(entry.str_value); + if (!postfix) + goto err_no_mem_clear; + + os_free(wpa_s->conf->p2p_ssid_postfix); + wpa_s->conf->p2p_ssid_postfix = postfix; + + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_SSID_POSTFIX; + } else if ((os_strcmp(entry.key, "IntraBss") == 0) && + (entry.type == DBUS_TYPE_BOOLEAN)) { + wpa_s->conf->p2p_intra_bss = entry.bool_value; + wpa_s->conf->changed_parameters |= + CFG_CHANGED_P2P_INTRA_BSS; + } else if ((os_strcmp(entry.key, "GroupIdle") == 0) && + (entry.type == DBUS_TYPE_UINT32)) + wpa_s->conf->p2p_group_idle = entry.uint32_value; + else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 && + entry.type == DBUS_TYPE_UINT32) + wpa_s->conf->disassoc_low_ack = entry.uint32_value; + else + goto error; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (wpa_s->conf->changed_parameters) { + /* Some changed parameters requires to update config*/ + wpa_supplicant_update_config(wpa_s); + } + + return TRUE; + + error: + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + wpa_dbus_dict_entry_clear(&entry); + return FALSE; + + err_no_mem_clear: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + wpa_dbus_dict_entry_clear(&entry); + return FALSE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct p2p_data *p2p = wpa_s->global->p2p; + int next = 0, i = 0; + int num = 0, out_of_mem = 0; + const u8 *addr; + const struct p2p_peer_info *peer_info = NULL; + dbus_bool_t success = FALSE; + + struct dl_list peer_objpath_list; + struct peer_objpath_node { + struct dl_list list; + char path[WPAS_DBUS_OBJECT_PATH_MAX]; + } *node, *tmp; + + char **peer_obj_paths = NULL; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + return FALSE; + + dl_list_init(&peer_objpath_list); + + /* Get the first peer info */ + peer_info = p2p_get_peer_found(p2p, NULL, next); + + /* Get next and accumulate them */ + next = 1; + while (peer_info != NULL) { + node = os_zalloc(sizeof(struct peer_objpath_node)); + if (!node) { + out_of_mem = 1; + goto error; + } + + addr = peer_info->p2p_device_addr; + os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART + "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(addr)); + dl_list_add_tail(&peer_objpath_list, &node->list); + num++; + + peer_info = p2p_get_peer_found(p2p, addr, next); + } + + /* + * Now construct the peer object paths in a form suitable for + * array_property_getter helper below. + */ + peer_obj_paths = os_zalloc(num * sizeof(char *)); + + if (!peer_obj_paths) { + out_of_mem = 1; + goto error; + } + + dl_list_for_each_safe(node, tmp, &peer_objpath_list, + struct peer_objpath_node, list) + peer_obj_paths[i++] = node->path; + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + peer_obj_paths, num, + error); + +error: + if (peer_obj_paths) + os_free(peer_obj_paths); + + dl_list_for_each_safe(node, tmp, &peer_objpath_list, + struct peer_objpath_node, list) { + dl_list_del(&node->list); + os_free(node); + } + if (out_of_mem) + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + + return success; +} + + +enum wpas_p2p_role { + WPAS_P2P_ROLE_DEVICE, + WPAS_P2P_ROLE_GO, + WPAS_P2P_ROLE_CLIENT, +}; + +static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!ssid) + return WPAS_P2P_ROLE_DEVICE; + if (wpa_s->wpa_state != WPA_COMPLETED) + return WPAS_P2P_ROLE_DEVICE; + + switch (ssid->mode) { + case WPAS_MODE_P2P_GO: + case WPAS_MODE_P2P_GROUP_FORMATION: + return WPAS_P2P_ROLE_GO; + case WPAS_MODE_INFRA: + if (ssid->p2p_group) + return WPAS_P2P_ROLE_CLIENT; + return WPAS_P2P_ROLE_DEVICE; + default: + return WPAS_P2P_ROLE_DEVICE; + } +} + + +dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *str; + + switch (wpas_get_p2p_role(wpa_s)) { + case WPAS_P2P_ROLE_GO: + str = "GO"; + break; + case WPAS_P2P_ROLE_CLIENT: + str = "client"; + break; + default: + str = "device"; + } + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str, + error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; + char *dbus_groupobj_path = path_buf; + + if (wpa_s->dbus_groupobj_path == NULL) + os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "/"); + else + os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s", wpa_s->dbus_groupobj_path); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &dbus_groupobj_path, error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT) + os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/"); + else + os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" + COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr)); + + path = go_peer_obj_path; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &path, error); +} + + +/* + * Peer object properties accessor methods + */ + +dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + char *tmp; + + if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error)) + return FALSE; + + /* get the peer info */ + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + tmp = os_strdup(info->device_name); + if (!tmp) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp, + error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + os_free(tmp); + return FALSE; + } + + os_free(tmp); + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + (char *) + info->pri_dev_type, + WPS_DEV_TYPE_LEN, error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, + &info->config_methods, error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &info->level, error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE, + &info->dev_capab, error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE, + &info->group_capab, error)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + DBusMessageIter variant_iter, array_iter; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 1", __func__); + return FALSE; + } + + if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 2", __func__); + return FALSE; + } + + if (info->wps_sec_dev_type_list_len) { + const u8 *sec_dev_type_list = info->wps_sec_dev_type_list; + int num_sec_device_types = + info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; + int i; + DBusMessageIter inner_array_iter; + + for (i = 0; i < num_sec_device_types; i++) { + if (!dbus_message_iter_open_container( + &array_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &inner_array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct " + "message 3 (%d)", + __func__, i); + return FALSE; + } + + if (!dbus_message_iter_append_fixed_array( + &inner_array_iter, DBUS_TYPE_BYTE, + &sec_dev_type_list, WPS_DEV_TYPE_LEN)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct " + "message 4 (%d)", + __func__, i); + return FALSE; + } + + if (!dbus_message_iter_close_container( + &array_iter, &inner_array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct " + "message 5 (%d)", + __func__, i); + return FALSE; + } + + sec_dev_type_list += WPS_DEV_TYPE_LEN; + } + } + + if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 6", __func__); + return FALSE; + } + + if (!dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 7", __func__); + return FALSE; + } + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT]; + int i, num; + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (info == NULL) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "failed to find peer"); + return FALSE; + } + + /* Add WPS vendor extensions attribute */ + for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (info->wps_vendor_ext[i] == NULL) + continue; + vendor_extension[num] = info->wps_vendor_ext[i]; + num++; + } + + if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE, + vendor_extension, + num, error)) + return FALSE; + + return TRUE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + dbus_bool_t success; + /* struct peer_handler_args *peer_args = user_data; */ + + success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + NULL, 0, error); + return success; +} + + +/** + * wpas_dbus_getter_persistent_groups - Get array of persistent group objects + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "PersistentGroups" property. + */ +dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct wpa_ssid *ssid; + char **paths; + unsigned int i = 0, num = 0; + dbus_bool_t success = FALSE; + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "dbus: %s: " + "An error occurred getting persistent groups list", + __func__); + dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error " + "occurred getting persistent groups list"); + return FALSE; + } + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + if (network_is_persistent_group(ssid)) + num++; + + paths = os_zalloc(num * sizeof(char *)); + if (!paths) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + /* Loop through configured networks and append object path of each */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!network_is_persistent_group(ssid)) + continue; + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (paths[i] == NULL) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + goto out; + } + /* Construct the object path for this network. */ + os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", + wpa_s->dbus_new_path, ssid->id); + } + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, num, error); + +out: + while (i) + os_free(paths[--i]); + os_free(paths); + return success; +} + + +/** + * wpas_dbus_getter_persistent_group_properties - Get options for a persistent + * group + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Properties" property of a persistent group. + */ +dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct network_handler_args *net = user_data; + + /* Leveraging the fact that persistent group object is still + * represented in same manner as network within. + */ + return wpas_dbus_getter_network_properties(iter, error, net); +} + + +/** + * wpas_dbus_setter_persistent_group_properties - Get options for a persistent + * group + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter for "Properties" property of a persistent group. + */ +dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct network_handler_args *net = user_data; + struct wpa_ssid *ssid = net->ssid; + DBusMessageIter variant_iter; + + /* + * Leveraging the fact that persistent group object is still + * represented in same manner as network within. + */ + dbus_message_iter_recurse(iter, &variant_iter); + return set_network_properties(net->wpa_s, ssid, &variant_iter, error); +} + + +/** + * wpas_dbus_new_iface_add_persistent_group - Add a new configured + * persistent_group + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing the object path of the new + * persistent group + * + * Handler function for "AddPersistentGroup" method call of a P2P Device + * interface. + */ +DBusMessage * wpas_dbus_handler_add_persistent_group( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_ssid *ssid = NULL; + char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; + DBusError error; + + dbus_message_iter_init(message, &iter); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "dbus: %s: " + "Cannot add new persistent group", __func__); + reply = wpas_dbus_error_unknown_error( + message, + "wpa_supplicant could not add " + "a persistent group on this interface."); + goto err; + } + + /* Mark the ssid as being a persistent group before the notification */ + ssid->disabled = 2; + ssid->p2p_persistent_group = 1; + wpas_notify_persistent_group_added(wpa_s, ssid); + + wpa_config_set_network_defaults(ssid); + + dbus_error_init(&error); + if (!set_network_properties(wpa_s, ssid, &iter, &error)) { + wpa_printf(MSG_DEBUG, "dbus: %s: " + "Control interface could not set persistent group " + "properties", __func__); + reply = wpas_dbus_reply_new_from_error(message, &error, + DBUS_ERROR_INVALID_ARGS, + "Failed to set network " + "properties"); + dbus_error_free(&error); + goto err; + } + + /* Construct the object path for this network. */ + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", + wpa_s->dbus_new_path, ssid->id); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) { + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + NULL); + goto err; + } + + return reply; + +err: + if (ssid) { + wpas_notify_persistent_group_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + } + return reply; +} + + +/** + * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent + * group + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemovePersistentGroup" method call of a P2P Device + * interface. + */ +DBusMessage * wpas_dbus_handler_remove_persistent_group( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + const char *op; + char *iface = NULL, *persistent_group_id = NULL; + int id; + struct wpa_ssid *ssid; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID); + + /* + * Extract the network ID and ensure the network is actually a child of + * this interface. + */ + iface = wpas_dbus_new_decompose_object_path(op, 1, + &persistent_group_id, + NULL); + if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + id = strtoul(persistent_group_id, NULL, 10); + if (errno == EINVAL) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_error_persistent_group_unknown(message); + goto out; + } + + wpas_notify_persistent_group_removed(wpa_s, ssid); + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + wpa_printf(MSG_ERROR, "dbus: %s: " + "error occurred when removing persistent group %d", + __func__, id); + reply = wpas_dbus_error_unknown_error( + message, + "error removing the specified persistent group on " + "this interface."); + goto out; + } + +out: + os_free(iface); + os_free(persistent_group_id); + return reply; +} + + +static void remove_persistent_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + wpas_notify_persistent_group_removed(wpa_s, ssid); + + if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { + wpa_printf(MSG_ERROR, "dbus: %s: " + "error occurred when removing persistent group %d", + __func__, ssid->id); + return; + } +} + + +/** + * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured + * persistent groups + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveAllPersistentGroups" method call of a + * P2P Device interface. + */ +DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid, *next; + struct wpa_config *config; + + config = wpa_s->conf; + ssid = config->ssid; + while (ssid) { + next = ssid->next; + if (network_is_persistent_group(ssid)) + remove_persistent_group(wpa_s, ssid); + ssid = next; + } + return NULL; +} + + +/* + * Group object properties accessor methods + */ + +dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct wpa_ssid *ssid; + unsigned int num_members; + char **paths; + unsigned int i; + void *next = NULL; + const u8 *addr; + dbus_bool_t success = FALSE; + + /* Verify correct role for this property */ + if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) { + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error); + } + + ssid = wpa_s->conf->ssid; + /* At present WPAS P2P_GO mode only applicable for p2p_go */ + if (ssid->mode != WPAS_MODE_P2P_GO && + ssid->mode != WPAS_MODE_AP && + ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) + return FALSE; + + num_members = p2p_get_group_num_members(wpa_s->p2p_group); + + paths = os_zalloc(num_members * sizeof(char *)); + if (!paths) + goto out_of_memory; + + i = 0; + while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) { + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (!paths[i]) + goto out_of_memory; + os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART + "/" COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(addr)); + i++; + } + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, num_members, + error); + + for (i = 0; i < num_members; i++) + os_free(paths[i]); + os_free(paths); + return success; + +out_of_memory: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + if (paths) { + for (i = 0; i < num_members; i++) + os_free(paths[i]); + os_free(paths); + } + return FALSE; +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + if (wpa_s->current_ssid == NULL) + return FALSE; + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len, error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + u8 role = wpas_get_p2p_role(wpa_s); + u8 *p_bssid; + + if (role == WPAS_P2P_ROLE_CLIENT) { + if (wpa_s->current_ssid == NULL) + return FALSE; + p_bssid = wpa_s->current_ssid->bssid; + } else { + if (wpa_s->ap_iface == NULL) + return FALSE; + p_bssid = wpa_s->ap_iface->bss[0]->own_addr; + } + + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + p_bssid, ETH_ALEN, + error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + u16 op_freq; + u8 role = wpas_get_p2p_role(wpa_s); + + if (role == WPAS_P2P_ROLE_CLIENT) { + if (wpa_s->go_params == NULL) + return FALSE; + op_freq = wpa_s->go_params->freq; + } else { + if (wpa_s->ap_iface == NULL) + return FALSE; + op_freq = wpa_s->ap_iface->freq; + } + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, + &op_freq, error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + u8 role = wpas_get_p2p_role(wpa_s); + char *p_pass = NULL; + + /* Verify correct role for this property */ + if (role == WPAS_P2P_ROLE_GO) { + if (wpa_s->current_ssid == NULL) + return FALSE; + p_pass = wpa_s->current_ssid->passphrase; + } else + p_pass = ""; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &p_pass, error); + +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + u8 role = wpas_get_p2p_role(wpa_s); + u8 *p_psk = NULL; + u8 psk_len = 0; + + /* Verify correct role for this property */ + if (role == WPAS_P2P_ROLE_CLIENT) { + if (wpa_s->current_ssid == NULL) + return FALSE; + p_psk = wpa_s->current_ssid->psk; + psk_len = 32; + } + + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + &p_psk, psk_len, error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct hostapd_data *hapd; + struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int num_vendor_ext = 0; + int i; + + /* Verify correct role for this property */ + if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) { + if (wpa_s->ap_iface == NULL) + return FALSE; + hapd = wpa_s->ap_iface->bss[0]; + + /* Parse WPS Vendor Extensions sent in Beacon/Probe Response */ + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (hapd->conf->wps_vendor_ext[i] == NULL) + vendor_ext[i] = NULL; + else { + vendor_ext[num_vendor_ext++] = + hapd->conf->wps_vendor_ext[i]; + } + } + } + + /* Return vendor extensions or no data */ + return wpas_dbus_simple_array_array_property_getter(iter, + DBUS_TYPE_BYTE, + vendor_ext, + num_vendor_ext, + error); +} + + +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + DBusMessageIter variant_iter, iter_dict; + struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; + unsigned int i; + struct hostapd_data *hapd = NULL; + + if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO && + wpa_s->ap_iface != NULL) + hapd = wpa_s->ap_iface->bss[0]; + else + return FALSE; + + dbus_message_iter_recurse(iter, &variant_iter); + if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) + return FALSE; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; + } + + if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY || + entry.array_len > MAX_WPS_VENDOR_EXTENSIONS) + goto error; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + if (i < entry.array_len) { + hapd->conf->wps_vendor_ext[i] = + entry.binarray_value[i]; + entry.binarray_value[i] = NULL; + } else + hapd->conf->wps_vendor_ext[i] = NULL; + } + + hostapd_update_wps(hapd); + } else + goto error; + + wpa_dbus_dict_entry_clear(&entry); + } + + return TRUE; + +error: + wpa_dbus_dict_entry_clear(&entry); + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; +} + + +DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + int upnp = 0; + int bonjour = 0; + char *service = NULL; + struct wpabuf *query = NULL; + struct wpabuf *resp = NULL; + u8 version = 0; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "service_type") && + (entry.type == DBUS_TYPE_STRING)) { + if (!os_strcmp(entry.str_value, "upnp")) + upnp = 1; + else if (!os_strcmp(entry.str_value, "bonjour")) + bonjour = 1; + else + goto error_clear; + wpa_dbus_dict_entry_clear(&entry); + } + } + + if (upnp == 1) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "version") && + entry.type == DBUS_TYPE_INT32) + version = entry.uint32_value; + else if (!os_strcmp(entry.key, "service") && + entry.type == DBUS_TYPE_STRING) + service = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + } + if (version <= 0 || service == NULL) + goto error; + + if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0) + goto error; + + os_free(service); + } else if (bonjour == 1) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "query")) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != DBUS_TYPE_BYTE)) + goto error_clear; + query = wpabuf_alloc_copy( + entry.bytearray_value, + entry.array_len); + } else if (!os_strcmp(entry.key, "response")) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != DBUS_TYPE_BYTE)) + goto error_clear; + resp = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + } + + wpa_dbus_dict_entry_clear(&entry); + } + + if (query == NULL || resp == NULL) + goto error; + + if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { + wpabuf_free(query); + wpabuf_free(resp); + goto error; + } + } else + goto error; + + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + return wpas_dbus_error_invalid_args(message, NULL); +} + + +DBusMessage * wpas_dbus_handler_p2p_delete_service( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + int upnp = 0; + int bonjour = 0; + int ret = 0; + char *service = NULL; + struct wpabuf *query = NULL; + u8 version = 0; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "service_type") && + (entry.type == DBUS_TYPE_STRING)) { + if (!os_strcmp(entry.str_value, "upnp")) + upnp = 1; + else if (!os_strcmp(entry.str_value, "bonjour")) + bonjour = 1; + else + goto error_clear; + wpa_dbus_dict_entry_clear(&entry); + } + } + if (upnp == 1) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!os_strcmp(entry.key, "version") && + entry.type == DBUS_TYPE_INT32) + version = entry.uint32_value; + else if (!os_strcmp(entry.key, "service") && + entry.type == DBUS_TYPE_STRING) + service = os_strdup(entry.str_value); + else + goto error_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (version <= 0 || service == NULL) + goto error; + + ret = wpas_p2p_service_del_upnp(wpa_s, version, service); + os_free(service); + if (ret != 0) + goto error; + } else if (bonjour == 1) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "query")) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != DBUS_TYPE_BYTE)) + goto error_clear; + query = wpabuf_alloc_copy( + entry.bytearray_value, + entry.array_len); + } else + goto error_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (query == NULL) + goto error; + + ret = wpas_p2p_service_del_bonjour(wpa_s, query); + if (ret != 0) + goto error; + wpabuf_free(query); + } else + goto error; + + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + return wpas_dbus_error_invalid_args(message, NULL); +} + + +DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + wpas_p2p_service_flush(wpa_s); + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_service_sd_req( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + int upnp = 0; + char *service = NULL; + char *peer_object_path = NULL; + struct wpabuf *tlv = NULL; + u8 version = 0; + u64 ref = 0; + u8 addr_buf[ETH_ALEN], *addr; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + if (!os_strcmp(entry.key, "peer_object") && + entry.type == DBUS_TYPE_OBJECT_PATH) { + peer_object_path = os_strdup(entry.str_value); + } else if (!os_strcmp(entry.key, "service_type") && + entry.type == DBUS_TYPE_STRING) { + if (!os_strcmp(entry.str_value, "upnp")) + upnp = 1; + else + goto error_clear; + } else if (!os_strcmp(entry.key, "version") && + entry.type == DBUS_TYPE_INT32) { + version = entry.uint32_value; + } else if (!os_strcmp(entry.key, "service") && + entry.type == DBUS_TYPE_STRING) { + service = os_strdup(entry.str_value); + } else if (!os_strcmp(entry.key, "tlv")) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) + goto error_clear; + tlv = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + } else + goto error_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + + if (!peer_object_path) { + addr = NULL; + } else { + if (parse_peer_object_path(peer_object_path, addr_buf) < 0 || + !p2p_peer_known(wpa_s->global->p2p, addr_buf)) + goto error; + + addr = addr_buf; + } + + if (upnp == 1) { + if (version <= 0 || service == NULL) + goto error; + + ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, addr, + version, + service); + } else { + if (tlv == NULL) + goto error; + ref = (unsigned long)wpas_p2p_sd_request(wpa_s, addr, tlv); + wpabuf_free(tlv); + } + + if (ref != 0) { + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT64, + &ref, DBUS_TYPE_INVALID); + } else { + reply = wpas_dbus_error_unknown_error( + message, "Unable to send SD request"); + } +out: + os_free(service); + os_free(peer_object_path); + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + if (tlv) + wpabuf_free(tlv); + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_service_sd_res( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + struct wpabuf *tlv = NULL; + int freq = 0; + int dlg_tok = 0; + u8 addr[ETH_ALEN]; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "peer_object") && + entry.type == DBUS_TYPE_OBJECT_PATH) { + peer_object_path = os_strdup(entry.str_value); + } else if (!os_strcmp(entry.key, "frequency") && + entry.type == DBUS_TYPE_INT32) { + freq = entry.uint32_value; + } else if (!os_strcmp(entry.key, "dialog_token") && + entry.type == DBUS_TYPE_UINT32) { + dlg_tok = entry.uint32_value; + } else if (!os_strcmp(entry.key, "tlvs")) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) + goto error_clear; + tlv = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + } else + goto error_clear; + + wpa_dbus_dict_entry_clear(&entry); + } + if (!peer_object_path || + (parse_peer_object_path(peer_object_path, addr) < 0) || + !p2p_peer_known(wpa_s->global->p2p, addr)) + goto error; + + if (tlv == NULL) + goto error; + + wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv); + wpabuf_free(tlv); +out: + os_free(peer_object_path); + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + u64 req = 0; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &req); + + if (req == 0) + goto error; + + if (!wpas_p2p_sd_cancel_request(wpa_s, (void *)(unsigned long) req)) + goto error; + + return NULL; +error: + return wpas_dbus_error_invalid_args(message, NULL); +} + + +DBusMessage * wpas_dbus_handler_p2p_service_update( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + wpas_p2p_sd_service_update(wpa_s); + return NULL; +} + + +DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + int ext = 0; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &ext); + + wpa_s->p2p_sd_over_ctrl_iface = ext; + + return NULL; + +} diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h new file mode 100644 index 0000000..241dd75 --- /dev/null +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -0,0 +1,217 @@ + +/* + * WPA Supplicant / dbus-based control interface for p2p + * + * 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. + */ + +#ifndef DBUS_NEW_HANDLERS_P2P_H +#define DBUS_NEW_HANDLERS_P2P_H + +struct peer_handler_args { + struct wpa_supplicant *wpa_s; + u8 p2p_device_addr[ETH_ALEN]; +}; + +struct groupmember_handler_args { + struct wpa_supplicant *wpa_s; + u8 member_addr[ETH_ALEN]; +}; + +/* + * P2P Device methods + */ + +DBusMessage *wpas_dbus_handler_p2p_find( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_stop_find( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_rejectpeer( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_listen( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_extendedlisten( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_presence_request( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_prov_disc_req( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_group_add( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_connect( + DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_invite( + DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_disconnect( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_flush( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_add_service( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_delete_service( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_flush_service( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_service_sd_req( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_service_sd_res( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_service_sd_cancel_req( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_service_update( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage *wpas_dbus_handler_p2p_serv_disc_external( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +/* + * P2P Device property accessor methods. + */ +dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +/* + * P2P Peer properties. + */ + +dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( + DBusMessageIter *iter, DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( + DBusMessageIter *iter, DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +/* + * P2P Group properties + */ + +dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +/* + * P2P Persistent Groups and properties + */ + +dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +DBusMessage * wpas_dbus_handler_add_persistent_group( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_persistent_group( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( + DBusMessage *message, struct wpa_supplicant *wpa_s); + + +#endif /* DBUS_NEW_HANDLERS_P2P_H */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index dc44a59..a72cfb3 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -19,6 +19,8 @@ #include "../config.h" #include "../wpa_supplicant_i.h" #include "../wps_supplicant.h" +#include "../driver_i.h" +#include "../ap.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" @@ -30,6 +32,7 @@ struct wps_start_params { int type; /* 0 - not set, 1 - pin, 2 - pbc */ u8 *bssid; char *pin; + u8 *p2p_dev_addr; }; @@ -107,7 +110,7 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, dbus_message_iter_recurse(entry_iter, &variant_iter); if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&variant_iter) != - DBUS_TYPE_ARRAY) { + DBUS_TYPE_BYTE) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, " "byte array required"); *reply = wpas_dbus_error_invalid_args( @@ -148,6 +151,41 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message, } +#ifdef CONFIG_P2P +static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message, + DBusMessageIter *entry_iter, + struct wps_start_params *params, + DBusMessage **reply) +{ + DBusMessageIter variant_iter, array_iter; + int len; + + dbus_message_iter_recurse(entry_iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&variant_iter) != + DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong " + "P2PDeviceAddress type, byte array required"); + *reply = wpas_dbus_error_invalid_args( + message, "P2PDeviceAddress must be a byte array"); + return -1; + } + dbus_message_iter_recurse(&variant_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, ¶ms->p2p_dev_addr, + &len); + if (len != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong " + "P2PDeviceAddress length %d", len); + *reply = wpas_dbus_error_invalid_args(message, + "P2PDeviceAddress " + "has wrong length"); + return -1; + } + return 0; +} +#endif /* CONFIG_P2P */ + + static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key, DBusMessageIter *entry_iter, struct wps_start_params *params, @@ -165,6 +203,11 @@ static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key, else if (os_strcmp(key, "Pin") == 0) return wpas_dbus_handler_wps_pin(message, entry_iter, params, reply); +#ifdef CONFIG_P2P + else if (os_strcmp(key, "P2PDeviceAddress") == 0) + return wpas_dbus_handler_wps_p2p_dev_addr(message, entry_iter, + params, reply); +#endif /* CONFIG_P2P */ wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key); *reply = wpas_dbus_error_invalid_args(message, key); @@ -231,11 +274,31 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, NULL); else if (params.type == 1) { - ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin); - if (ret > 0) - os_snprintf(npin, sizeof(npin), "%08d", ret); - } else - ret = wpas_wps_start_pbc(wpa_s, params.bssid); +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + ret = wpa_supplicant_ap_wps_pin(wpa_s, + params.bssid, + params.pin, + npin, sizeof(npin)); + else +#endif /* CONFIG_AP */ + { + ret = wpas_wps_start_pin(wpa_s, params.bssid, + params.pin, 0, + DEV_PW_DEFAULT); + if (ret > 0) + os_snprintf(npin, sizeof(npin), "%08d", ret); + } + } else { +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + ret = wpa_supplicant_ap_wps_pbc(wpa_s, + params.bssid, + params.p2p_dev_addr); + else +#endif /* CONFIG_AP */ + ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0); + } if (ret < 0) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in " @@ -283,40 +346,43 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, * wpas_dbus_getter_process_credentials - Check if credentials are processed * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure - * Returns: DBus message with a boolean on success or DBus error on failure + * Returns: TRUE on success, FALSE on failure * * Getter for "ProcessCredentials" property. Returns returned boolean will be * true if wps_cred_processing configuration field is not equal to 1 or false * if otherwise. */ -DBusMessage * wpas_dbus_getter_process_credentials( - DBusMessage *message, struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, + DBusError *error, + void *user_data) { + struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1); - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN, - &process); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &process, error); } /** * wpas_dbus_setter_process_credentials - Set credentials_processed conf param - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: NULL on success or DBus error on failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure * * Setter for "ProcessCredentials" property. Sets credentials_processed on 2 * if boolean argument is true or on 1 if otherwise. */ -DBusMessage * wpas_dbus_setter_process_credentials( - DBusMessage *message, struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - DBusMessage *reply = NULL; + struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process_credentials, old_pc; - reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN, - &process_credentials); - if (reply) - return reply; + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN, + &process_credentials)) + return FALSE; old_pc = (wpa_s->conf->wps_cred_processing != 1); wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1); @@ -327,5 +393,5 @@ DBusMessage * wpas_dbus_setter_process_credentials( WPAS_DBUS_NEW_IFACE_WPS, "ProcessCredentials"); - return NULL; + return TRUE; } diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c index 06749db..e254365 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/wpa_supplicant/dbus/dbus_new_helpers.c @@ -21,112 +21,50 @@ #include "dbus_common_i.h" #include "dbus_new.h" #include "dbus_new_helpers.h" +#include "dbus_dict_helpers.h" -/** - * recursive_iter_copy - Reads arguments from one iterator and - * writes to another recursively - * @from: iterator to read from - * @to: iterator to write to - * - * Copies one iterator's elements to another. If any element in - * iterator is of container type, its content is copied recursively - */ -static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to) -{ - - char *subtype = NULL; - int type; - - /* iterate over iterator to copy */ - while ((type = dbus_message_iter_get_arg_type(from)) != - DBUS_TYPE_INVALID) { - - /* simply copy basic type entries */ - if (dbus_type_is_basic(type)) { - if (dbus_type_is_fixed(type)) { - /* - * According to DBus documentation all - * fixed-length types are guaranteed to fit - * 8 bytes - */ - dbus_uint64_t v; - dbus_message_iter_get_basic(from, &v); - dbus_message_iter_append_basic(to, type, &v); - } else { - char *v; - dbus_message_iter_get_basic(from, &v); - dbus_message_iter_append_basic(to, type, &v); - } - } else { - /* recursively copy container type entries */ - DBusMessageIter write_subiter, read_subiter; - - dbus_message_iter_recurse(from, &read_subiter); - - if (type == DBUS_TYPE_VARIANT || - type == DBUS_TYPE_ARRAY) { - subtype = dbus_message_iter_get_signature( - &read_subiter); - } - - dbus_message_iter_open_container(to, type, subtype, - &write_subiter); - - recursive_iter_copy(&read_subiter, &write_subiter); - - dbus_message_iter_close_container(to, &write_subiter); - if (subtype) - dbus_free(subtype); - } - - dbus_message_iter_next(from); - } -} - - -static unsigned int fill_dict_with_properties( - DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props, - const char *interface, const void *user_data) +static dbus_bool_t fill_dict_with_properties( + DBusMessageIter *dict_iter, + const struct wpa_dbus_property_desc *props, + const char *interface, void *user_data, DBusError *error) { - DBusMessage *reply; - DBusMessageIter entry_iter, ret_iter; - unsigned int counter = 0; + DBusMessageIter entry_iter; const struct wpa_dbus_property_desc *dsc; for (dsc = props; dsc && dsc->dbus_property; dsc++) { - if (!os_strncmp(dsc->dbus_interface, interface, - WPAS_DBUS_INTERFACE_MAX) && - dsc->access != W && dsc->getter) { - reply = dsc->getter(NULL, user_data); - if (!reply) - continue; - - if (dbus_message_get_type(reply) == - DBUS_MESSAGE_TYPE_ERROR) { - dbus_message_unref(reply); - continue; - } + /* Only return properties for the requested D-Bus interface */ + if (os_strncmp(dsc->dbus_interface, interface, + WPAS_DBUS_INTERFACE_MAX) != 0) + continue; - dbus_message_iter_init(reply, &ret_iter); + /* Skip write-only properties */ + if (dsc->getter == NULL) + continue; - dbus_message_iter_open_container(dict_iter, - DBUS_TYPE_DICT_ENTRY, - NULL, &entry_iter); - dbus_message_iter_append_basic( - &entry_iter, DBUS_TYPE_STRING, - &dsc->dbus_property); + if (!dbus_message_iter_open_container(dict_iter, + DBUS_TYPE_DICT_ENTRY, + NULL, &entry_iter)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + return FALSE; + } + if (!dbus_message_iter_append_basic(&entry_iter, + DBUS_TYPE_STRING, + &dsc->dbus_property)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + return FALSE; + } - recursive_iter_copy(&ret_iter, &entry_iter); + /* An error getting a property fails the request entirely */ + if (!dsc->getter(&entry_iter, error, user_data)) + return FALSE; - dbus_message_iter_close_container(dict_iter, - &entry_iter); - dbus_message_unref(reply); - counter++; - } + dbus_message_iter_close_container(dict_iter, &entry_iter); } - return counter; + return TRUE; } @@ -142,37 +80,44 @@ static unsigned int fill_dict_with_properties( * specified as argument. Returned message contains one dict argument * with properties names as keys and theirs values as values. */ -static DBusMessage * get_all_properties( - DBusMessage *message, char *interface, - struct wpa_dbus_object_desc *obj_dsc) +static DBusMessage * get_all_properties(DBusMessage *message, char *interface, + struct wpa_dbus_object_desc *obj_dsc) { - /* Create and initialize the return message */ - DBusMessage *reply = dbus_message_new_method_return(message); + DBusMessage *reply; DBusMessageIter iter, dict_iter; - int props_num; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict_iter); + DBusError error; - props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties, - interface, obj_dsc->user_data); + reply = dbus_message_new_method_return(message); + if (reply == NULL) { + wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply", + __func__); + return NULL; + } - dbus_message_iter_close_container(&iter, &dict_iter); + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { + wpa_printf(MSG_ERROR, "%s: out of memory creating reply", + __func__); + dbus_message_unref(reply); + reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + "out of memory"); + return reply; + } - if (props_num == 0) { + dbus_error_init(&error); + if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, + interface, obj_dsc->user_data, &error)) + { dbus_message_unref(reply); - reply = dbus_message_new_error(message, - DBUS_ERROR_INVALID_ARGS, - "No readable properties in " - "this interface"); + reply = wpas_dbus_reply_new_from_error(message, &error, + DBUS_ERROR_INVALID_ARGS, + "No readable properties" + " in this interface"); + dbus_error_free(&error); + return reply; } + wpa_dbus_dict_close_write(&iter, &dict_iter); return reply; } @@ -219,15 +164,33 @@ static DBusMessage * properties_get(DBusMessage *message, const struct wpa_dbus_property_desc *dsc, void *user_data) { - if (os_strcmp(dbus_message_get_signature(message), "ss")) + DBusMessage *reply; + DBusMessageIter iter; + DBusError error; + + if (os_strcmp(dbus_message_get_signature(message), "ss")) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, NULL); + } + + if (dsc->getter == NULL) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Property is write-only"); + } - if (dsc->access != W && dsc->getter) - return dsc->getter(message, user_data); + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &iter); + + dbus_error_init(&error); + if (dsc->getter(&iter, &error, user_data) == FALSE) { + dbus_message_unref(reply); + reply = wpas_dbus_reply_new_from_error( + message, &error, DBUS_ERROR_FAILED, + "Failed to read property"); + dbus_error_free(&error); + } - return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, - "Property is write-only"); + return reply; } @@ -235,15 +198,38 @@ static DBusMessage * properties_set(DBusMessage *message, const struct wpa_dbus_property_desc *dsc, void *user_data) { - if (os_strcmp(dbus_message_get_signature(message), "ssv")) + DBusMessage *reply; + DBusMessageIter iter; + DBusError error; + + if (os_strcmp(dbus_message_get_signature(message), "ssv")) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, NULL); + } + + if (dsc->setter == NULL) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Property is read-only"); + } + + dbus_message_iter_init(message, &iter); + /* Skip the interface name and the property name */ + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); - if (dsc->access != R && dsc->setter) - return dsc->setter(message, user_data); + /* Iter will now point to the property's new value */ + dbus_error_init(&error); + if (dsc->setter(&iter, &error, user_data) == TRUE) { + /* Success */ + reply = dbus_message_new_method_return(message); + } else { + reply = wpas_dbus_reply_new_from_error( + message, &error, DBUS_ERROR_FAILED, + "Failed to set property"); + dbus_error_free(&error); + } - return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, - "Property is read-only"); + return reply; } @@ -552,6 +538,7 @@ int wpa_dbus_register_object_per_iface( struct wpa_dbus_object_desc *obj_desc) { DBusConnection *con; + DBusError error; DBusObjectPathVTable vtable = { &free_dbus_object_desc_cb, &message_handler, @@ -566,14 +553,24 @@ int wpa_dbus_register_object_per_iface( obj_desc->connection = con; obj_desc->path = os_strdup(path); + dbus_error_init(&error); /* Register the message handler for the interface functions */ - if (!dbus_connection_register_object_path(con, path, &vtable, - obj_desc)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler for interface %s object %s", ifname, path); + if (!dbus_connection_try_register_object_path(con, path, &vtable, + obj_desc, &error)) { + if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { + wpa_printf(MSG_DEBUG, "dbus: %s", error.message); + } else { + wpa_printf(MSG_ERROR, "dbus: Could not set up message " + "handler for interface %s object %s", + ifname, path); + wpa_printf(MSG_ERROR, "dbus error: %s", error.name); + wpa_printf(MSG_ERROR, "dbus: %s", error.message); + } + dbus_error_free(&error); return -1; } + dbus_error_free(&error); return 0; } @@ -611,14 +608,14 @@ int wpa_dbus_unregister_object_per_iface( } -static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc, - const char *interface, - DBusMessageIter *dict_iter) +static dbus_bool_t put_changed_properties( + const struct wpa_dbus_object_desc *obj_dsc, const char *interface, + DBusMessageIter *dict_iter, int clear_changed) { - DBusMessage *getter_reply; - DBusMessageIter prop_iter, entry_iter; + DBusMessageIter entry_iter; const struct wpa_dbus_property_desc *dsc; int i; + DBusError error; for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property; dsc++, i++) { @@ -627,43 +624,94 @@ static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc, continue; if (os_strcmp(dsc->dbus_interface, interface) != 0) continue; - obj_dsc->prop_changed_flags[i] = 0; - - getter_reply = dsc->getter(NULL, obj_dsc->user_data); - if (!getter_reply || - dbus_message_get_type(getter_reply) == - DBUS_MESSAGE_TYPE_ERROR) { - wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value " - "of property %s", __func__, - dsc->dbus_property); - continue; - } + if (clear_changed) + obj_dsc->prop_changed_flags[i] = 0; - if (!dbus_message_iter_init(getter_reply, &prop_iter) || - !dbus_message_iter_open_container(dict_iter, + if (!dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, - NULL, &entry_iter) || - !dbus_message_iter_append_basic(&entry_iter, + NULL, &entry_iter)) + return FALSE; + + if (!dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &dsc->dbus_property)) - goto err; - - recursive_iter_copy(&prop_iter, &entry_iter); + return FALSE; + + dbus_error_init(&error); + if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { + if (dbus_error_is_set (&error)) { + wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " + "new value of property %s: (%s) %s", + __func__, dsc->dbus_property, + error.name, error.message); + } else { + wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " + "new value of property %s", + __func__, dsc->dbus_property); + } + dbus_error_free(&error); + return FALSE; + } if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) - goto err; - - dbus_message_unref(getter_reply); + return FALSE; } + return TRUE; +} + + +static void do_send_prop_changed_signal( + DBusConnection *con, const char *path, const char *interface, + const struct wpa_dbus_object_desc *obj_dsc) +{ + DBusMessage *msg; + DBusMessageIter signal_iter, dict_iter; + + msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES, + "PropertiesChanged"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &signal_iter); + + if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING, + &interface)) + goto err; + + /* Changed properties dict */ + if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "{sv}", &dict_iter)) + goto err; + + if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) + goto err; + + if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) + goto err; + + /* Invalidated properties array (empty) */ + if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "s", &dict_iter)) + goto err; + + if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) + goto err; + + dbus_connection_send(con, msg, NULL); + +out: + dbus_message_unref(msg); return; err: - wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__); + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + goto out; } -static void send_prop_changed_signal( +static void do_send_deprecated_prop_changed_signal( DBusConnection *con, const char *path, const char *interface, const struct wpa_dbus_object_desc *obj_dsc) { @@ -680,7 +728,8 @@ static void send_prop_changed_signal( "{sv}", &dict_iter)) goto err; - put_changed_properties(obj_dsc, interface, &dict_iter); + if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) + goto err; if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) goto err; @@ -698,6 +747,29 @@ err: } +static void send_prop_changed_signal( + DBusConnection *con, const char *path, const char *interface, + const struct wpa_dbus_object_desc *obj_dsc) +{ + /* + * First, send property change notification on the standardized + * org.freedesktop.DBus.Properties interface. This call will not + * clear the property change bits, so that they are preserved for + * the call that follows. + */ + do_send_prop_changed_signal(con, path, interface, obj_dsc); + + /* + * Now send PropertiesChanged on our own interface for backwards + * compatibility. This is deprecated and will be removed in a future + * release. + */ + do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc); + + /* Property change bits have now been cleared. */ +} + + static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) { DBusConnection *con = eloop_ctx; @@ -849,27 +921,147 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, * @iface: dbus priv struct * @path: path to DBus object which properties will be obtained * @interface: interface name which properties will be obtained - * @dict_iter: correct, open DBus dictionary iterator. + * @iter: DBus message iter at which to append property dictionary. * * Iterates over all properties registered with object and execute getters * of those, which are readable and which interface matches interface * specified as argument. Obtained properties values are stored in * dict_iter dictionary. */ -void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, - const char *path, const char *interface, - DBusMessageIter *dict_iter) +dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, + const char *path, + const char *interface, + DBusMessageIter *iter) { struct wpa_dbus_object_desc *obj_desc = NULL; + DBusMessageIter dict_iter; + DBusError error; dbus_connection_get_object_path_data(iface->con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: " - "could not obtain object's private data: %s", path); - return; + wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's " + "private data: %s", __func__, path); + return FALSE; + } + + if (!wpa_dbus_dict_open_write(iter, &dict_iter)) { + wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict", + __func__); + return FALSE; + } + + dbus_error_init(&error); + if (!fill_dict_with_properties(&dict_iter, obj_desc->properties, + interface, obj_desc->user_data, + &error)) { + wpa_printf(MSG_ERROR, "dbus: %s: failed to get object" + " properties: (%s) %s", __func__, + dbus_error_is_set(&error) ? error.name : "none", + dbus_error_is_set(&error) ? error.message : "none"); + dbus_error_free(&error); + return FALSE; } - fill_dict_with_properties(dict_iter, obj_desc->properties, - interface, obj_desc->user_data); + return wpa_dbus_dict_close_write(iter, &dict_iter); +} + +/** + * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts + * @path: The dbus object path + * @p2p_persistent_group: indicates whether to parse the path as a P2P + * persistent group object + * @network: (out) the configured network this object path refers to, if any + * @bssid: (out) the scanned bssid this object path refers to, if any + * Returns: The object path of the network interface this path refers to + * + * For a given object path, decomposes the object path into object id, network, + * and BSSID parts, if those parts exist. + */ +char *wpas_dbus_new_decompose_object_path(const char *path, + int p2p_persistent_group, + char **network, + char **bssid) +{ + const unsigned int dev_path_prefix_len = + os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); + char *obj_path_only; + char *next_sep; + + /* Be a bit paranoid about path */ + if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", + dev_path_prefix_len)) + return NULL; + + /* Ensure there's something at the end of the path */ + if ((path + dev_path_prefix_len)[0] == '\0') + return NULL; + + obj_path_only = os_strdup(path); + if (obj_path_only == NULL) + return NULL; + + next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); + if (next_sep != NULL) { + const char *net_part = os_strstr( + next_sep, p2p_persistent_group ? + WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" : + WPAS_DBUS_NEW_NETWORKS_PART "/"); + const char *bssid_part = os_strstr( + next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); + + if (network && net_part) { + /* Deal with a request for a configured network */ + const char *net_name = net_part + + os_strlen(p2p_persistent_group ? + WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART + "/" : + WPAS_DBUS_NEW_NETWORKS_PART "/"); + *network = NULL; + if (os_strlen(net_name)) + *network = os_strdup(net_name); + } else if (bssid && bssid_part) { + /* Deal with a request for a scanned BSSID */ + const char *bssid_name = bssid_part + + os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); + if (os_strlen(bssid_name)) + *bssid = os_strdup(bssid_name); + else + *bssid = NULL; + } + + /* Cut off interface object path before "/" */ + *next_sep = '\0'; + } + + return obj_path_only; +} + + +/** + * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a + * dbus error structure + * @message: The original request message for which the error is a reply + * @error: The error containing a name and a descriptive error cause + * @fallback_name: A generic error name if @error was not set + * @fallback_string: A generic error string if @error was not set + * Returns: A new D-Bus error message + * + * Given a DBusMessage structure, creates a new D-Bus error message using + * the error name and string contained in that structure. + */ +DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message, + DBusError *error, + const char *fallback_name, + const char *fallback_string) +{ + if (error && error->name && error->message) { + return dbus_message_new_error(message, error->name, + error->message); + } + if (fallback_name && fallback_string) { + return dbus_message_new_error(message, fallback_name, + fallback_string); + } + return NULL; } diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h index 8db7a37..d6e7b48 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/wpa_supplicant/dbus/dbus_new_helpers.h @@ -22,8 +22,9 @@ typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message, void *user_data); typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg); -typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message, - const void *user_data); +typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter, + DBusError *error, + void *user_data); struct wpa_dbus_object_desc { DBusConnection *connection; @@ -44,8 +45,6 @@ struct wpa_dbus_object_desc { WPADBusArgumentFreeFunction user_data_free_func; }; -enum dbus_prop_access { R, W, RW }; - enum dbus_arg_direction { ARG_IN, ARG_OUT }; struct wpa_dbus_argument { @@ -67,7 +66,7 @@ struct wpa_dbus_method_desc { /* method handling function */ WPADBusMethodHandler method_handler; /* array of arguments */ - struct wpa_dbus_argument args[3]; + struct wpa_dbus_argument args[4]; }; /** @@ -79,7 +78,7 @@ struct wpa_dbus_signal_desc { /* signal interface */ const char *dbus_interface; /* array of arguments */ - struct wpa_dbus_argument args[3]; + struct wpa_dbus_argument args[4]; }; /** @@ -96,14 +95,13 @@ struct wpa_dbus_property_desc { WPADBusPropertyAccessor getter; /* property setter function */ WPADBusPropertyAccessor setter; - /* property access permissions */ - enum dbus_prop_access access; }; #define WPAS_DBUS_OBJECT_PATH_MAX 150 #define WPAS_DBUS_INTERFACE_MAX 150 #define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50 +#define WPAS_DBUS_AUTH_MODE_MAX 64 #define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable" #define WPA_DBUS_INTROSPECTION_METHOD "Introspect" @@ -127,9 +125,10 @@ int wpa_dbus_unregister_object_per_iface( struct wpas_dbus_priv *ctrl_iface, const char *path); -void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, - const char *path, const char *interface, - DBusMessageIter *dict_iter); +dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, + const char *path, + const char *interface, + DBusMessageIter *iter); void wpa_dbus_flush_all_changed_properties(DBusConnection *con); @@ -144,4 +143,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, DBusMessage * wpa_dbus_introspect(DBusMessage *message, struct wpa_dbus_object_desc *obj_dsc); +char *wpas_dbus_new_decompose_object_path(const char *path, + int p2p_persistent_group, + char **network, + char **bssid); + +DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message, + DBusError *error, + const char *fallback_name, + const char *fallback_string); + #endif /* WPA_DBUS_CTRL_H */ diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c index c660c04..d443269 100644 --- a/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/wpa_supplicant/dbus/dbus_new_introspect.c @@ -43,7 +43,7 @@ static struct interfaces * add_interface(struct dl_list *list, iface = os_zalloc(sizeof(struct interfaces)); if (!iface) return NULL; - iface->xml = wpabuf_alloc(3000); + iface->xml = wpabuf_alloc(6000); if (iface->xml == NULL) { os_free(iface); return NULL; @@ -89,10 +89,11 @@ static void add_entry(struct wpabuf *xml, const char *type, const char *name, static void add_property(struct wpabuf *xml, const struct wpa_dbus_property_desc *dsc) { - wpabuf_printf(xml, "", + wpabuf_printf(xml, "", dsc->dbus_property, dsc->type, - (dsc->access == R ? "read" : - (dsc->access == W ? "write" : "readwrite"))); + dsc->getter ? "read" : "", + dsc->setter ? "write" : ""); } @@ -163,6 +164,12 @@ static void add_interfaces(struct dl_list *list, struct wpabuf *xml) if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) { wpabuf_put_buf(xml, iface->xml); wpabuf_put_str(xml, ""); + } else { + wpa_printf(MSG_DEBUG, "dbus: Not enough room for " + "add_interfaces inspect data: tailroom %u, " + "add %u", + (unsigned int) wpabuf_tailroom(xml), + (unsigned int) wpabuf_len(iface->xml)); } dl_list_del(&iface->list); wpabuf_free(iface->xml); @@ -250,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(4000); + xml = wpabuf_alloc(10000); if (xml == NULL) return NULL; diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c index 7f25bf0..d255e14 100644 --- a/wpa_supplicant/dbus/dbus_old.c +++ b/wpa_supplicant/dbus/dbus_old.c @@ -287,6 +287,8 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, else if (!os_strcmp(method, "wpsReg")) reply = wpas_dbus_iface_wps_reg(message, wpa_s); #endif /* CONFIG_WPS */ + else if (!os_strcmp(method, "flush")) + reply = wpas_dbus_iface_flush(message, wpa_s); } /* If the message was handled, send back the reply */ @@ -547,6 +549,59 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WPS */ +void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, + int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct wpas_dbus_priv *iface; + DBusMessage *_signal = NULL; + const char *hash; + const char *cert_hex; + int cert_hex_len; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s->global == NULL) + return; + iface = wpa_s->global->dbus; + if (iface == NULL) + return; + + _signal = dbus_message_new_signal(wpa_s->dbus_path, + WPAS_DBUS_IFACE_INTERFACE, + "Certification"); + if (_signal == NULL) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_certification: " + "Could not create dbus signal; likely out of " + "memory"); + return; + } + + hash = cert_hash ? cert_hash : ""; + cert_hex = cert ? wpabuf_head(cert) : ""; + cert_hex_len = cert ? wpabuf_len(cert) : 0; + + if (!dbus_message_append_args(_signal, + DBUS_TYPE_INT32,&depth, + DBUS_TYPE_STRING, &subject, + DBUS_TYPE_STRING, &hash, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &cert_hex, cert_hex_len, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_ERROR, + "dbus: wpa_supplicant_dbus_notify_certification: " + "Not enough memory to construct signal"); + goto out; + } + + dbus_connection_send(iface->con, _signal, NULL); + +out: + dbus_message_unref(_signal); + +} + /** * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h index a9840c2..9523867 100644 --- a/wpa_supplicant/dbus/dbus_old.h +++ b/wpa_supplicant/dbus/dbus_old.h @@ -82,6 +82,10 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, enum wpa_states old_state); void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, const struct wps_credential *cred); +void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, + int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert); char * wpas_dbus_decompose_object_path(const char *path, char **network, char **bssid); @@ -114,6 +118,14 @@ wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, { } +static inline void +wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, + int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ +} + static inline int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) { diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c index d914697..a7eabf3 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/wpa_supplicant/dbus/dbus_old_handlers.c @@ -116,7 +116,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, DBusMessageIter iter_dict; struct wpa_dbus_dict_entry entry; - if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) @@ -922,7 +922,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, dbus_message_iter_init(message, &iter); - if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) { + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } @@ -1202,7 +1202,7 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules( if (!dbus_message_iter_init(message, &iter)) goto error; - if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { @@ -1324,7 +1324,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, dbus_message_iter_init(message, &iter); - if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) return wpas_dbus_new_invalid_opts_error(message, NULL); while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { @@ -1434,3 +1434,35 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, return wpas_dbus_new_success_reply(message); } + + +/** + * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "flush" method call. Handles requests for an + * interface with an optional "age" parameter that specifies the minimum + * age of a BSS to be flushed. + */ +DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int flush_age = 0; + + if (os_strlen(dbus_message_get_signature(message)) != 0 && + !dbus_message_get_args(message, NULL, + DBUS_TYPE_INT32, &flush_age, + DBUS_TYPE_INVALID)) { + return wpas_dbus_new_invalid_opts_error(message, NULL); + } + + if (flush_age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, flush_age); + + return wpas_dbus_new_success_reply(message); +} diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h index 65e876f..009e807 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.h +++ b/wpa_supplicant/dbus/dbus_old_handlers.h @@ -96,6 +96,9 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message); DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, const char *arg); diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c index b5879f3..c04b844 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c @@ -43,9 +43,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, return wpas_dbus_new_invalid_opts_error(message, NULL); if (!os_strcmp(arg_bssid, "any")) - ret = wpas_wps_start_pbc(wpa_s, NULL); + ret = wpas_wps_start_pbc(wpa_s, NULL, 0); else if (!hwaddr_aton(arg_bssid, bssid)) - ret = wpas_wps_start_pbc(wpa_s, bssid); + ret = wpas_wps_start_pbc(wpa_s, bssid, 0); else { return wpas_dbus_new_invalid_opts_error(message, "Invalid BSSID"); @@ -94,9 +94,11 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, } if (os_strlen(pin) > 0) - ret = wpas_wps_start_pin(wpa_s, _bssid, pin); + ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, + DEV_PW_DEFAULT); else - ret = wpas_wps_start_pin(wpa_s, _bssid, NULL); + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, + DEV_PW_DEFAULT); if (ret < 0) { return dbus_message_new_error(message, diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service deleted file mode 100644 index a9ce1ec..0000000 --- a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=fi.epitest.hostap.WPASupplicant -Exec=/sbin/wpa_supplicant -u -User=root diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in new file mode 100644 index 0000000..a75918f --- /dev/null +++ b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=fi.epitest.hostap.WPASupplicant +Exec=@BINDIR@/wpa_supplicant -u +User=root +SystemdService=wpa_supplicant.service diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service deleted file mode 100644 index df78471..0000000 --- a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=fi.w1.wpa_supplicant1 -Exec=/sbin/wpa_supplicant -u -User=root diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in new file mode 100644 index 0000000..d97ff39 --- /dev/null +++ b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=fi.w1.wpa_supplicant1 +Exec=@BINDIR@/wpa_supplicant -u +User=root +SystemdService=wpa_supplicant.service diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 8c32cb3..0476bc3 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -78,10 +78,15 @@ CONFIG_DRIVER_ATMEL=y #CONFIG_DRIVER_RALINK=y # Driver interface for generic Linux wireless extensions +# Note: WEXT is deprecated in the current Linux kernel version and no new +# functionality is added to it. nl80211-based interface is the new +# replacement for WEXT and its use allows wpa_supplicant to properly control +# the driver to improve existing functionality like roaming and to support new +# functionality. CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface -#CONFIG_DRIVER_NL80211=y +CONFIG_DRIVER_NL80211=y # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y @@ -109,11 +114,6 @@ CONFIG_DRIVER_WEXT=y # Driver interface for development testing #CONFIG_DRIVER_TEST=y -# Include client MLME (management frame processing) for test driver -# This can be used to test MLME operations in hostapd with the test interface. -# space. -#CONFIG_CLIENT_MLME=y - # Driver interface for wired Ethernet drivers CONFIG_DRIVER_WIRED=y @@ -123,6 +123,10 @@ CONFIG_DRIVER_WIRED=y # Driver interface for no driver (e.g., WPS ER only) #CONFIG_DRIVER_NONE=y +# Solaris libraries +#LIBS += -lsocket -ldlpi -lnsl +#LIBS_c += -lsocket + # Enable IEEE 802.1X Supplicant (automatically included if any EAP method is # included) CONFIG_IEEE8021X_EAPOL=y @@ -161,6 +165,9 @@ CONFIG_EAP_OTP=y # EAP-PSK (experimental; this is _not_ needed for WPA-PSK) #CONFIG_EAP_PSK=y +# EAP-pwd (secure authentication using only a password) +#CONFIG_EAP_PWD=y + # EAP-PAX #CONFIG_EAP_PAX=y @@ -190,6 +197,13 @@ CONFIG_EAP_LEAP=y # Wi-Fi Protected Setup (WPS) #CONFIG_WPS=y +# Enable WSC 2.0 support +#CONFIG_WPS2=y +# Enable WPS external registrar functionality +#CONFIG_WPS_ER=y +# Disable credentials for an open network by default when acting as a WPS +# registrar. +#CONFIG_WPS_REG_DISABLE_OPEN=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y @@ -224,6 +238,10 @@ CONFIG_CTRL_IFACE=y # the resulting binary. #CONFIG_READLINE=y +# Include internal line edit mode in wpa_cli. This can be used as a replacement +# for GNU Readline to provide limited command line editing and history support. +#CONFIG_WPA_CLI_EDIT=y + # Remove debugging code that is printing out debug message to stdout. # This can be used to reduce the size of the wpa_supplicant considerably # if debugging code is not needed. The size reduction can be around 35% @@ -305,18 +323,17 @@ CONFIG_PEERKEY=y # Select TLS implementation # openssl = OpenSSL (default) -# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA) +# gnutls = GnuTLS # internal = Internal TLSv1 implementation (experimental) # none = Empty template #CONFIG_TLS=openssl -# Whether to enable TLS/IA support, which is required for EAP-TTLSv1. -# You need CONFIG_TLS=gnutls for this to have any effect. Please note that -# even though the core GnuTLS library is released under LGPL, this extra -# library uses GPL and as such, the terms of GPL apply to the combination -# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not -# apply for distribution of the resulting binary. -#CONFIG_GNUTLS_EXTRA=y +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. It should be noted that some existing TLS v1.0 -based +# implementation may not be compatible with TLS v1.1 message (ClientHello is +# sent prior to negotiating which version will be used) +#CONFIG_TLSV11=y # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of @@ -378,6 +395,11 @@ CONFIG_PEERKEY=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) #CONFIG_DEBUG_FILE=y +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y +# Set syslog facility for debug messages +#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON + # Enable privilege separation (see README 'Privilege separation' for details) #CONFIG_PRIVSEP=y @@ -389,7 +411,7 @@ CONFIG_PEERKEY=y # This tracks use of memory allocations and other registrations and reports # incorrect use with a backtrace of call (or allocation) location. #CONFIG_WPA_TRACE=y -# For BSD, comment out these. +# For BSD, uncomment these. #LIBS += -lexecinfo #LIBS_p += -lexecinfo #LIBS_c += -lexecinfo @@ -398,7 +420,47 @@ CONFIG_PEERKEY=y # This enables use of libbfd to get more detailed symbols for the backtraces # generated by CONFIG_WPA_TRACE=y. #CONFIG_WPA_TRACE_BFD=y -# For BSD, comment out these. +# For BSD, uncomment these. #LIBS += -lbfd -liberty -lz #LIBS_p += -lbfd -liberty -lz #LIBS_c += -lbfd -liberty -lz + +# wpa_supplicant depends on strong random number generation being available +# from the operating system. os_get_random() function is used to fetch random +# data when needed, e.g., for key generation. On Linux and BSD systems, this +# works by reading /dev/urandom. It should be noted that the OS entropy pool +# needs to be properly initialized before wpa_supplicant is started. This is +# important especially on embedded devices that do not have a hardware random +# number generator and may by default start up with minimal entropy available +# for random number generation. +# +# As a safety net, wpa_supplicant is by default trying to internally collect +# additional entropy for generating random data to mix in with the data fetched +# from the OS. This by itself is not considered to be very strong, but it may +# help in cases where the system pool is not initialized properly. However, it +# is very strongly recommended that the system pool is initialized with enough +# entropy either by using hardware assisted random number generator or by +# storing state over device reboots. +# +# wpa_supplicant can be configured to maintain its own entropy store over +# restarts to enhance random number generation. This is not perfect, but it is +# much more secure than using the same sequence of random numbers after every +# reboot. This can be enabled with -e command line option. The +# specified file needs to be readable and writable by wpa_supplicant. +# +# If the os_get_random() is known to provide strong random data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal wpa_supplicant random pool can be +# disabled. This will save some in binary size and CPU use. However, this +# should only be considered for builds that are known to be used on devices +# that meet the requirements described above. +#CONFIG_NO_RANDOM_POOL=y + +# IEEE 802.11n (High Throughput) support (mainly for AP mode) +#CONFIG_IEEE80211N=y + +# Interworking (IEEE 802.11u) +# This can be used to enable functionality to improve interworking with +# external networks (GAS/ANQP to learn more about the networks and network +# selection based on available credentials). +#CONFIG_INTERWORKING=y diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 3aae51b..0ab4e15 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -268,13 +268,6 @@ - atmel - - ATMEL AT76C5XXx (USB, PCMCIA). - - - - wext Linux wireless extensions (generic). @@ -282,13 +275,6 @@ - ndiswrapper - - Linux ndiswrapper. - - - - broadcom Broadcom wl.o driver. @@ -296,13 +282,6 @@ - ipw - - Intel ipw2100/2200 driver. - - - - wired wpa_supplicant wired Ethernet driver @@ -599,13 +578,6 @@ wpa_supplicant \ - ATMEL AT76C5XXx driver for USB and PCMCIA cards - - (http://atmelwlandriver.sourceforge.net/). - - - - Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with Windows diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index a70aa6a..9828aff 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -79,6 +79,23 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + u32 interval) +{ + if (wpa_s->driver->sched_scan) + return wpa_s->driver->sched_scan(wpa_s->drv_priv, + params, interval); + return -1; +} + +static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->stop_sched_scan) + return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv); + return -1; +} + static inline struct wpa_scan_results * wpa_drv_get_scan_results2( struct wpa_supplicant *wpa_s) { @@ -236,35 +253,6 @@ wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes, return NULL; } -static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s, - enum hostapd_hw_mode phymode, int chan, - int freq) -{ - if (wpa_s->driver->set_channel) - return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode, - chan, freq); - return -1; -} - -static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s, - const u8 *ssid, size_t ssid_len) -{ - if (wpa_s->driver->set_ssid) { - return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid, - ssid_len); - } - return -1; -} - -static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s, - const u8 *bssid) -{ - if (wpa_s->driver->set_bssid) { - return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid); - } - return -1; -} - static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, const char *alpha2) { @@ -282,24 +270,6 @@ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s, - const u8 *addr, const u8 *supp_rates, - size_t supp_rates_len) -{ - if (wpa_s->driver->mlme_add_sta) - return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr, - supp_rates, supp_rates_len); - return -1; -} - -static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s, - const u8 *addr) -{ - if (wpa_s->driver->mlme_remove_sta) - return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr); - return -1; -} - static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, const u8 *ies, size_t ies_len) @@ -320,15 +290,11 @@ static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s, - const u8 *head, size_t head_len, - const u8 *tail, size_t tail_len, - int dtim_period, int beacon_int) +static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s, + struct wpa_driver_ap_params *params) { - if (wpa_s->driver->set_beacon) - return wpa_s->driver->set_beacon(wpa_s->drv_priv, head, - head_len, tail, tail_len, - dtim_period, beacon_int); + if (wpa_s->driver->set_ap) + return wpa_s->driver->set_ap(wpa_s->drv_priv, params); return -1; } @@ -351,12 +317,12 @@ static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s, static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) + const u8 *own_addr, u32 flags) { if (wpa_s->driver->hapd_send_eapol) return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr, data, data_len, encrypt, - own_addr); + own_addr, flags); return -1; } @@ -383,14 +349,30 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s, static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, + int no_cck) { if (wpa_s->driver->send_action) return wpa_s->driver->send_action(wpa_s->drv_priv, freq, - dst, src, bssid, data, - data_len); + wait, dst, src, bssid, + data, data_len, no_cck); + return -1; +} + +static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->send_action_cancel_wait) + wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s, + struct hostapd_freq_params *freq) +{ + if (wpa_s->driver->set_freq) + return wpa_s->driver->set_freq(wpa_s->drv_priv, freq); return -1; } @@ -398,12 +380,12 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, char *force_ifname, - u8 *if_addr) + u8 *if_addr, const char *bridge) { if (wpa_s->driver->if_add) return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname, addr, bss_ctx, NULL, force_ifname, - if_addr); + if_addr, bridge); return -1; } @@ -444,15 +426,6 @@ static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s, - int disabled) -{ - if (wpa_s->driver->disable_11b_rates) - return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv, - disabled); - return -1; -} - static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s) { if (wpa_s->driver->deinit_ap) @@ -460,6 +433,13 @@ static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s) return 0; } +static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->deinit_p2p_cli) + return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv); + return 0; +} + static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s) { if (wpa_s->driver->suspend) @@ -481,14 +461,213 @@ static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s, + struct wpa_signal_info *si) +{ + if (wpa_s->driver->signal_poll) + return wpa_s->driver->signal_poll(wpa_s->drv_priv, si); + return -1; +} + static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { if (!wpa_s->driver->set_ap_wps_ie) return -1; return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon, - proberesp); + proberesp, assocresp); +} + +static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->shared_freq) + return -1; + return wpa_s->driver->shared_freq(wpa_s->drv_priv); +} + +static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s, + u8 *buf, size_t buf_len) +{ + if (!wpa_s->driver->get_noa) + return -1; + return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len); +} + +static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s, + int legacy_ps, int opp_ps, + int ctwindow) +{ + if (!wpa_s->driver->set_p2p_powersave) + return -1; + return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps, + opp_ps, ctwindow); +} + +static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu) +{ + if (!wpa_s->driver->ampdu) + return -1; + return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu); +} + +static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s, + unsigned int timeout, int type) +{ + if (!wpa_s->driver->p2p_find) + return -1; + return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type); +} + +static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->p2p_stop_find) + return -1; + return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv); +} + +static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s, + unsigned int timeout) +{ + if (!wpa_s->driver->p2p_listen) + return -1; + return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout); +} + +static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int wps_method, + int go_intent, + const u8 *own_interface_addr, + unsigned int force_freq, + int persistent_group) +{ + if (!wpa_s->driver->p2p_connect) + return -1; + return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr, + wps_method, go_intent, + own_interface_addr, force_freq, + persistent_group); +} + +static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + if (!wpa_s->driver->wps_success_cb) + return -1; + return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr); +} + +static inline int +wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->p2p_group_formation_failed) + return -1; + return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv); +} + +static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s, + const struct p2p_params *params) +{ + if (!wpa_s->driver->p2p_set_params) + return -1; + return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params); +} + +static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + u16 config_methods, int join) +{ + if (!wpa_s->driver->p2p_prov_disc_req) + return -1; + return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr, + config_methods, join); +} + +static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s, + const u8 *dst, + const struct wpabuf *tlvs) +{ + if (!wpa_s->driver->p2p_sd_request) + return 0; + return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs); +} + +static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, + u64 req) +{ + if (!wpa_s->driver->p2p_sd_cancel_request) + return -1; + return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req); +} + +static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s, + int freq, const u8 *dst, + u8 dialog_token, + const struct wpabuf *resp_tlvs) +{ + if (!wpa_s->driver->p2p_sd_response) + return -1; + return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst, + dialog_token, resp_tlvs); +} + +static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->p2p_service_update) + return -1; + return wpa_s->driver->p2p_service_update(wpa_s->drv_priv); +} + +static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->p2p_reject) + return -1; + return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr); +} + +static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s, + const u8 *peer, int role, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, + int persistent_group) +{ + if (!wpa_s->driver->p2p_invite) + return -1; + return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid, + ssid, ssid_len, go_dev_addr, + persistent_group); +} + +static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s, + const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) +{ + if (wpa_s->driver->send_tdls_mgmt) { + return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst, + action_code, dialog_token, + status_code, buf, len); + } + return -1; +} + +static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s, + enum tdls_oper oper, const u8 *peer) +{ + if (!wpa_s->driver->tdls_oper) + return -1; + return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer); +} + +static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s, + const u8 *kek, const u8 *kck, + const u8 *replay_ctr) +{ + if (!wpa_s->driver->set_rekey_info) + return; + wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr); } #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c index f668874..e5f43aa 100644 --- a/wpa_supplicant/eap_register.c +++ b/wpa_supplicant/eap_register.c @@ -130,6 +130,10 @@ int eap_register_methods(void) ret = eap_peer_tnc_register(); #endif /* EAP_TNC */ +#ifdef EAP_PWD + if (ret == 0) + ret = eap_peer_pwd_register(); +#endif /* EAP_PWD */ #ifdef EAP_SERVER_IDENTITY if (ret == 0) @@ -231,5 +235,10 @@ int eap_register_methods(void) ret = eap_server_tnc_register(); #endif /* EAP_SERVER_TNC */ +#ifdef EAP_SERVER_PWD + if (ret == 0) + ret = eap_server_pwd_register(); +#endif /* EAP_SERVER_PWD */ + return ret; } diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index 4eed854..024903c 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2011, Jouni Malinen * * 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 @@ -22,12 +22,15 @@ #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" +#include "eap_server/eap_methods.h" #include "eloop.h" +#include "utils/base64.h" #include "rsn_supp/wpa.h" #include "eap_peer/eap_i.h" #include "wpa_supplicant_i.h" #include "radius/radius.h" #include "radius/radius_client.h" +#include "common/wpa_ctrl.h" #include "ctrl_iface.h" #include "pcsc_funcs.h" @@ -74,6 +77,8 @@ struct eapol_test_data { char *connect_info; u8 own_addr[ETH_ALEN]; struct extra_radius_attr *extra_attrs; + + FILE *server_cert_file; }; static struct eapol_test_data eapol_test; @@ -279,7 +284,9 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, } } - radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr); + if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr) + < 0) + goto fail; return; fail: @@ -290,7 +297,6 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf, size_t len) { - /* struct wpa_supplicant *wpa_s = ctx; */ printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n", type, (unsigned long) len); if (type == IEEE802_1X_TYPE_EAP_PACKET) { @@ -304,16 +310,16 @@ static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf, static void eapol_test_set_config_blob(void *ctx, struct wpa_config_blob *blob) { - struct wpa_supplicant *wpa_s = ctx; - wpa_config_set_blob(wpa_s->conf, blob); + struct eapol_test_data *e = ctx; + wpa_config_set_blob(e->wpa_s->conf, blob); } static const struct wpa_config_blob * eapol_test_get_config_blob(void *ctx, const char *name) { - struct wpa_supplicant *wpa_s = ctx; - return wpa_config_get_blob(wpa_s->conf, name); + struct eapol_test_data *e = ctx; + return wpa_config_get_blob(e->wpa_s->conf, name); } @@ -382,6 +388,53 @@ static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) } +static void eapol_test_write_cert(FILE *f, const char *subject, + const struct wpabuf *cert) +{ + unsigned char *encoded; + + encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL); + if (encoded == NULL) + return; + fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s" + "-----END CERTIFICATE-----\n\n", subject, encoded); + os_free(encoded); +} + + +static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct eapol_test_data *e = ctx; + + wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s'%s%s", + depth, subject, + cert_hash ? " hash=" : "", + cert_hash ? cert_hash : ""); + + if (cert) { + char *cert_hex; + size_t len = wpabuf_len(cert) * 2 + 1; + cert_hex = os_malloc(len); + if (cert_hex) { + wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert), + wpabuf_len(cert)); + wpa_msg_ctrl(e->wpa_s, MSG_INFO, + WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s' cert=%s", + depth, subject, cert_hex); + os_free(cert_hex); + } + + if (e->server_cert_file) + eapol_test_write_cert(e->server_cert_file, + subject, cert); + } +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -393,7 +446,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, printf("Failed to allocate EAPOL context.\n"); return -1; } - ctx->ctx = wpa_s; + ctx->ctx = e; ctx->msg_ctx = wpa_s; ctx->scard_ctx = wpa_s->scard; ctx->cb = eapol_sm_cb; @@ -407,6 +460,8 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + ctx->cert_cb = eapol_test_cert_cb; + ctx->cert_in_cb = 1; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -963,7 +1018,7 @@ static void usage(void) "eapol_test [-nWS] -c [-a] [-p] " "[-s]\\\n" " [-r] [-t] [-C] \\\n" - " [-M] \\\n" + " [-M] [-o] \\\n" " [-A]\n" "eapol_test scard\n" @@ -989,6 +1044,8 @@ static void usage(void) " -M = Set own MAC address " "(Calling-Station-Id,\n" " default: 02:00:00:00:00:01)\n" + " -o = Write received server certificate\n" + " chain to the specified file\n" " -N = send arbitrary attribute specified by:\n" " attr_id:syntax:value or attr_id\n" " attr_id - number id of the attribute\n" @@ -1030,7 +1087,7 @@ int main(int argc, char *argv[]) wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W"); + c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W"); if (c < 0) break; switch (c) { @@ -1055,6 +1112,16 @@ int main(int argc, char *argv[]) case 'n': eapol_test.no_mppe_keys++; break; + case 'o': + if (eapol_test.server_cert_file) + fclose(eapol_test.server_cert_file); + eapol_test.server_cert_file = fopen(optarg, "w"); + if (eapol_test.server_cert_file == NULL) { + printf("Could not open '%s' for writing\n", + optarg); + return -1; + } + break; case 'p': as_port = atoi(optarg); break; @@ -1191,9 +1258,15 @@ int main(int argc, char *argv[]) test_eapol_clean(&eapol_test, &wpa_s); eap_peer_unregister_methods(); +#ifdef CONFIG_AP + eap_server_unregister_methods(); +#endif /* CONFIG_AP */ eloop_destroy(); + if (eapol_test.server_cert_file) + fclose(eapol_test.server_cert_file); + printf("MPPE keys OK: %d mismatch: %d\n", eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch); if (eapol_test.num_mppe_mismatch) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 85dcfb2..1c3b9eb 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2011, Jouni Malinen * * 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 @@ -28,18 +28,23 @@ #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "ap/hostapd.h" +#include "p2p/p2p.h" #include "notify.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" +#include "gas_query.h" +#include "p2p_supplicant.h" #include "bgscan.h" #include "ap.h" #include "bss.h" -#include "mlme.h" #include "scan.h" +#include "offchannel.h" static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) @@ -49,22 +54,22 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; - wpa_printf(MSG_DEBUG, "Select network based on association " - "information"); + wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " + "information"); ssid = wpa_supplicant_get_ssid(wpa_s); if (ssid == NULL) { - wpa_printf(MSG_INFO, "No network configuration found for the " - "current AP"); + wpa_msg(wpa_s, MSG_INFO, + "No network configuration found for the current AP"); return -1; } if (ssid->disabled) { - wpa_printf(MSG_DEBUG, "Selected network is disabled"); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); return -1; } - wpa_printf(MSG_DEBUG, "Network configuration found for the current " - "AP"); + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " + "current AP"); if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | @@ -91,8 +96,7 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) } -static void wpa_supplicant_stop_countermeasures(void *eloop_ctx, - void *sock_ctx) +void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -109,11 +113,34 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { int bssid_changed; +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + +#ifdef CONFIG_AP + wpa_supplicant_ap_deinit(wpa_s); +#endif /* CONFIG_AP */ + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return; + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); +#ifdef CONFIG_P2P + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); +#endif /* CONFIG_P2P */ wpa_s->current_bss = NULL; + wpa_s->assoc_freq = 0; +#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SME + if (wpa_s->sme.ft_ies) + sme_update_ft_ies(wpa_s, NULL, NULL, 0); +#endif /* CONFIG_SME */ +#endif /* CONFIG_IEEE80211R */ + if (bssid_changed) wpas_notify_bssid_changed(wpa_s); @@ -145,8 +172,8 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) } } - wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA " - "cache", pmksa_set == 0 ? "" : "not "); + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from " + "PMKSA cache", pmksa_set == 0 ? "" : "not "); } @@ -154,14 +181,15 @@ static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { if (data == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event"); + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate " + "event"); return; } - wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR - " index=%d preauth=%d", - MAC2STR(data->pmkid_candidate.bssid), - data->pmkid_candidate.index, - data->pmkid_candidate.preauth); + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR + " index=%d preauth=%d", + MAC2STR(data->pmkid_candidate.bssid), + data->pmkid_candidate.index, + data->pmkid_candidate.preauth); pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid, data->pmkid_candidate.index, @@ -204,6 +232,7 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { #ifdef IEEE8021X_EAPOL +#ifdef PCSC_FUNCS int aka = 0, sim = 0, type; if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) @@ -232,13 +261,14 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, aka = 0; if (!sim && !aka) { - wpa_printf(MSG_DEBUG, "Selected network is configured to use " - "SIM, but neither EAP-SIM nor EAP-AKA are enabled"); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to " + "use SIM, but neither EAP-SIM nor EAP-AKA are " + "enabled"); return 0; } - wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM " - "(sim=%d aka=%d) - initialize PCSC", sim, aka); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM " + "(sim=%d aka=%d) - initialize PCSC", sim, aka); if (sim && aka) type = SCARD_TRY_BOTH; else if (aka) @@ -248,12 +278,13 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, wpa_s->scard = scard_init(type); if (wpa_s->scard == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize SIM " - "(pcsc-lite)"); + wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); return -1; } wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); +#endif /* PCSC_FUNCS */ #endif /* IEEE8021X_EAPOL */ return 0; @@ -287,6 +318,9 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, privacy = 1; #endif /* IEEE8021X_EAPOL */ + if (wpa_key_mgmt_wpa(ssid->key_mgmt)) + privacy = 1; + if (bss->caps & IEEE80211_CAP_PRIVACY) return privacy; return !privacy; @@ -301,53 +335,70 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, int proto_match = 0; const u8 *rsn_ie, *wpa_ie; int ret; + int wep_ok; ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; + /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */ + wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) && + (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); + rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { - wpa_printf(MSG_DEBUG, " skip RSN IE - parse failed"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse " + "failed"); break; } + + if (wep_ok && + (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) + { + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " + "in RSN IE"); + return 1; + } + if (!(ie.proto & ssid->proto)) { - wpa_printf(MSG_DEBUG, " skip RSN IE - proto " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto " + "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { - wpa_printf(MSG_DEBUG, " skip RSN IE - PTK cipher " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK " + "cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { - wpa_printf(MSG_DEBUG, " skip RSN IE - GTK cipher " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK " + "cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { - wpa_printf(MSG_DEBUG, " skip RSN IE - key mgmt " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt " + "mismatch"); break; } #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { - wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame " - "protection"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " + "frame protection"); break; } #endif /* CONFIG_IEEE80211W */ - wpa_printf(MSG_DEBUG, " selected based on RSN IE"); + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); return 1; } @@ -356,39 +407,60 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { - wpa_printf(MSG_DEBUG, " skip WPA IE - parse failed"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse " + "failed"); break; } + + if (wep_ok && + (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) + { + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " + "in WPA IE"); + return 1; + } + if (!(ie.proto & ssid->proto)) { - wpa_printf(MSG_DEBUG, " skip WPA IE - proto " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto " + "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { - wpa_printf(MSG_DEBUG, " skip WPA IE - PTK cipher " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK " + "cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { - wpa_printf(MSG_DEBUG, " skip WPA IE - GTK cipher " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK " + "cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { - wpa_printf(MSG_DEBUG, " skip WPA IE - key mgmt " - "mismatch"); + wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt " + "mismatch"); break; } - wpa_printf(MSG_DEBUG, " selected based on WPA IE"); + wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE"); return 1; } - if (proto_match == 0) - wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match"); + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && + wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); + return 0; + } + + if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); + return 1; + } + + wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with " + "WPA/WPA2"); return 0; } @@ -408,237 +480,266 @@ static int freq_allowed(int *freqs, int freq) } -static struct wpa_bss * -wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, - struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) +static int ht_supported(const struct hostapd_hw_modes *mode) { - struct wpa_ssid *ssid; - struct wpa_scan_res *bss; - size_t i; - struct wpa_blacklist *e; - const u8 *ie; + if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports HT. Assume + * it does to avoid connection issues. + */ + return 1; + } - wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP"); - for (i = 0; i < scan_res->num; i++) { - const u8 *ssid_; - u8 wpa_ie_len, rsn_ie_len, ssid_len; - bss = scan_res->res[i]; + /* + * IEEE Std 802.11n-2009 20.1.1: + * An HT non-AP STA shall support all EQM rates for one spatial stream. + */ + return mode->mcs_set[0] == 0xff; +} - ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); - ssid_ = ie ? ie + 2 : (u8 *) ""; - ssid_len = ie ? ie[1] : 0; - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - wpa_ie_len = ie ? ie[1] : 0; +static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss) +{ + const struct hostapd_hw_modes *mode = NULL, *modes; + const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; + const u8 *rate_ie; + int i, j, k; - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); - rsn_ie_len = ie ? ie[1] : 0; + if (bss->freq == 0) + return 1; /* Cannot do matching without knowing band */ - wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", - (int) i, MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps); + modes = wpa_s->hw.modes; + if (modes == NULL) { + /* + * The driver does not provide any additional information + * about the utilized hardware, so allow the connection attempt + * to continue. + */ + return 1; + } - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e && e->count > 1) { - wpa_printf(MSG_DEBUG, " skip - blacklisted"); - continue; + for (i = 0; i < wpa_s->hw.num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + int freq = modes[i].channels[j].freq; + if (freq == bss->freq) { + if (mode && + mode->mode == HOSTAPD_MODE_IEEE80211G) + break; /* do not allow 802.11b replace + * 802.11g */ + mode = &modes[i]; + break; + } } + } - if (ssid_len == 0) { - wpa_printf(MSG_DEBUG, " skip - SSID not known"); - continue; - } + if (mode == NULL) + return 0; - if (wpa_ie_len == 0 && rsn_ie_len == 0) { - wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); + for (i = 0; i < (int) sizeof(scan_ie); i++) { + rate_ie = wpa_scan_get_ie(bss, scan_ie[i]); + if (rate_ie == NULL) continue; - } - for (ssid = group; ssid; ssid = ssid->pnext) { - int check_ssid = 1; + for (j = 2; j < rate_ie[1] + 2; j++) { + int flagged = !!(rate_ie[j] & 0x80); + int r = (rate_ie[j] & 0x7f) * 5; - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, " skip - disabled"); + /* + * IEEE Std 802.11n-2009 7.3.2.2: + * The new BSS Membership selector value is encoded + * like a legacy basic rate, but it is not a rate and + * only indicates if the BSS members are required to + * support the mandatory features of Clause 20 [HT PHY] + * in order to join the BSS. + */ + if (flagged && ((rate_ie[j] & 0x7f) == + BSS_MEMBERSHIP_SELECTOR_HT_PHY)) { + if (!ht_supported(mode)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support " + "HT PHY"); + return 0; + } continue; } -#ifdef CONFIG_WPS - if (ssid->ssid_len == 0 && - wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) - check_ssid = 0; -#endif /* CONFIG_WPS */ - - if (check_ssid && - (ssid_len != ssid->ssid_len || - os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "SSID mismatch"); + if (!flagged) continue; - } - if (ssid->bssid_set && - os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) - { - wpa_printf(MSG_DEBUG, " skip - " - "BSSID mismatch"); - continue; + /* check for legacy basic rates */ + for (k = 0; k < mode->num_rates; k++) { + if (mode->rates[k] == r) + break; } - - if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) - continue; - - if (!freq_allowed(ssid->freq_list, bss->freq)) { - wpa_printf(MSG_DEBUG, " skip - " - "frequency not allowed"); - continue; + if (k == mode->num_rates) { + /* + * IEEE Std 802.11-2007 7.3.2.2 demands that in + * order to join a BSS all required rates + * have to be supported by the hardware. + */ + wpa_dbg(wpa_s, MSG_DEBUG, " hardware does " + "not support required rate %d.%d Mbps", + r / 10, r % 10); + return 0; } - - wpa_printf(MSG_DEBUG, " selected WPA AP " - MACSTR " ssid='%s'", - MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len)); - *selected_ssid = ssid; - return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len); } } - return NULL; + return 1; } -static struct wpa_bss * -wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, - struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) +static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_scan_res *bss, + struct wpa_ssid *group) { - struct wpa_ssid *ssid; - struct wpa_scan_res *bss; - size_t i; + const u8 *ssid_; + u8 wpa_ie_len, rsn_ie_len, ssid_len; + int wpa; struct wpa_blacklist *e; const u8 *ie; + struct wpa_ssid *ssid; - wpa_printf(MSG_DEBUG, "Try to find non-WPA AP"); - for (i = 0; i < scan_res->num; i++) { - const u8 *ssid_; - u8 wpa_ie_len, rsn_ie_len, ssid_len; - bss = scan_res->res[i]; + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid_ = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; - ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); - ssid_ = ie ? ie + 2 : (u8 *) ""; - ssid_len = ie ? ie[1] : 0; + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie_len = ie ? ie[1] : 0; - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - wpa_ie_len = ie ? ie[1] : 0; + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); - rsn_ie_len = ie ? ie[1] : 0; + wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", + i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); - wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", - (int) i, MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len), - wpa_ie_len, rsn_ie_len, bss->caps); + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e) { + int limit = 1; + if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) { + /* + * When only a single network is enabled, we can + * trigger blacklisting on the first failure. This + * should not be done with multiple enabled networks to + * avoid getting forced to move into a worse ESS on + * single error if there are no other BSSes of the + * current ESS. + */ + limit = 0; + } + if (e->count > limit) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(count=%d limit=%d)", e->count, limit); + return NULL; + } + } + + if (ssid_len == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); + return NULL; + } - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e && e->count > 1) { - wpa_printf(MSG_DEBUG, " skip - blacklisted"); + wpa = wpa_ie_len > 0 || rsn_ie_len > 0; + + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + + if (ssid->disabled) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } - if (ssid_len == 0) { - wpa_printf(MSG_DEBUG, " skip - SSID not known"); +#ifdef CONFIG_WPS + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(WPS)"); continue; } - for (ssid = group; ssid; ssid = ssid->pnext) { - int check_ssid = ssid->ssid_len != 0; + if (wpa && ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, " skip - disabled"); - continue; - } - -#ifdef CONFIG_WPS - if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { - /* Only allow wildcard SSID match if an AP - * advertises active WPS operation that matches - * with our mode. */ - check_ssid = 1; - if (ssid->ssid_len == 0 && - wpas_wps_ssid_wildcard_ok(wpa_s, ssid, - bss)) - check_ssid = 0; - } + if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + /* Only allow wildcard SSID match if an AP + * advertises active WPS operation that matches + * with our mode. */ + check_ssid = 1; + if (ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; + } #endif /* CONFIG_WPS */ - if (check_ssid && - (ssid_len != ssid->ssid_len || - os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "SSID mismatch"); - continue; - } + if (ssid->bssid_set && ssid->ssid_len == 0 && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) + check_ssid = 0; - if (ssid->bssid_set && - os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) - { - wpa_printf(MSG_DEBUG, " skip - " - "BSSID mismatch"); - continue; - } + if (check_ssid && + (ssid_len != ssid->ssid_len || + os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); + continue; + } - if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && - !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && - !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) - { - wpa_printf(MSG_DEBUG, " skip - " - "non-WPA network not allowed"); - continue; - } + if (ssid->bssid_set && + os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); + continue; + } - if ((ssid->key_mgmt & - (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256)) && - (wpa_ie_len != 0 || rsn_ie_len != 0)) { - wpa_printf(MSG_DEBUG, " skip - " - "WPA network"); - continue; - } + if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) + continue; - if (!wpa_supplicant_match_privacy(bss, ssid)) { - wpa_printf(MSG_DEBUG, " skip - " - "privacy mismatch"); - continue; - } + if (!wpa && + !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network " + "not allowed"); + continue; + } - if (bss->caps & IEEE80211_CAP_IBSS) { - wpa_printf(MSG_DEBUG, " skip - " - "IBSS (adhoc) network"); - continue; - } + if (!wpa_supplicant_match_privacy(bss, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " + "mismatch"); + continue; + } - if (!freq_allowed(ssid->freq_list, bss->freq)) { - wpa_printf(MSG_DEBUG, " skip - " - "frequency not allowed"); - continue; - } + if (bss->caps & IEEE80211_CAP_IBSS) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) " + "network"); + continue; + } - wpa_printf(MSG_DEBUG, " selected non-WPA AP " - MACSTR " ssid='%s'", - MAC2STR(bss->bssid), - wpa_ssid_txt(ssid_, ssid_len)); - *selected_ssid = ssid; - return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len); + if (!freq_allowed(ssid->freq_list, bss->freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not " + "allowed"); + continue; } + + if (!rate_match(wpa_s, bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do " + "not match"); + continue; + } + +#ifdef CONFIG_P2P + /* + * TODO: skip the AP if its P2P IE has Group Formation + * bit set in the P2P Group Capability Bitmap and we + * are not in Group Formation with that device. + */ +#endif /* CONFIG_P2P */ + + /* Matching configuration found */ + return ssid; } + /* No matching configuration found */ return NULL; } @@ -649,21 +750,31 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { - struct wpa_bss *selected; + size_t i; - wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", - group->priority); + wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); - /* First, try to find WPA-enabled AP */ - selected = wpa_supplicant_select_bss_wpa(wpa_s, scan_res, group, - selected_ssid); - if (selected) - return selected; + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + const u8 *ie, *ssid; + u8 ssid_len; - /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration - * allows this. */ - return wpa_supplicant_select_bss_non_wpa(wpa_s, scan_res, group, - selected_ssid); + *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group); + if (!*selected_ssid) + continue; + + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + ssid = ie ? ie + 2 : (u8 *) ""; + ssid_len = ie ? ie[1] : 0; + + wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR + " ssid='%s'", + MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); + return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len); + } + + return NULL; } @@ -685,8 +796,8 @@ wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, } if (selected == NULL && wpa_s->blacklist) { - wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " - "and try again"); + wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear " + "blacklist and try again"); wpa_blacklist_clear(wpa_s); wpa_s->blacklist_cleared++; } else if (selected == NULL) @@ -713,15 +824,22 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, } -void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, - struct wpa_bss *selected, - struct wpa_ssid *ssid) +int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid) { if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); - wpa_supplicant_req_new_scan(wpa_s, 10, 0); - return; +#ifdef CONFIG_P2P + if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) + return -1; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS + wpas_wps_cancel(wpa_s); +#endif /* CONFIG_WPS */ + return -1; } /* @@ -731,18 +849,27 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, */ if (wpa_s->reassociate || (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && - (wpa_s->wpa_state != WPA_ASSOCIATING || + ((wpa_s->wpa_state != WPA_ASSOCIATING && + wpa_s->wpa_state != WPA_AUTHENTICATING) || os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != 0))) { if (wpa_supplicant_scard_init(wpa_s, ssid)) { wpa_supplicant_req_new_scan(wpa_s, 10, 0); - return; + return 0; } + wpa_msg(wpa_s, MSG_DEBUG, "Request association: " + "reassociate: %d selected: "MACSTR " bssid: " MACSTR + " pending: " MACSTR " wpa_state: %s", + wpa_s->reassociate, MAC2STR(selected->bssid), + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + wpa_supplicant_state_txt(wpa_s->wpa_state)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { - wpa_printf(MSG_DEBUG, "Already associated with the selected " - "AP"); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the " + "selected AP"); } + + return 0; } @@ -769,28 +896,25 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) /* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based * on BSS added and BSS changed events */ static void wpa_supplicant_rsn_preauth_scan_results( - struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) + struct wpa_supplicant *wpa_s) { - int i; + struct wpa_bss *bss; if (rsn_preauth_scan_results(wpa_s->wpa) < 0) return; - for (i = scan_res->num - 1; i >= 0; i--) { + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { const u8 *ssid, *rsn; - struct wpa_scan_res *r; - - r = scan_res->res[i]; - ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); + ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID); if (ssid == NULL) continue; - rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (rsn == NULL) continue; - rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn); + rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn); } } @@ -814,6 +938,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, if (wpa_s->current_ssid != ssid) return 1; /* different network block */ + if (wpas_driver_bss_selection(wpa_s)) + return 0; /* Driver-based roaming */ + for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *res = scan_res->res[i]; const u8 *ie; @@ -833,17 +960,17 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, if (!current_bss) return 1; /* current BSS not seen in scan results */ - wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation"); - wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d", - MAC2STR(current_bss->bssid), current_bss->level); - wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d", - MAC2STR(selected->bssid), selected->level); + wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); + wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d", + MAC2STR(current_bss->bssid), current_bss->level); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d", + MAC2STR(selected->bssid), selected->level); if (wpa_s->current_ssid->bssid_set && os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has " - "preferred BSSID"); + wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS " + "has preferred BSSID"); return 1; } @@ -861,16 +988,16 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, min_diff = 5; } if (abs(current_bss->level - selected->level) < min_diff) { - wpa_printf(MSG_DEBUG, "Skip roam - too small difference in " - "signal level"); + wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " + "in signal level"); return 0; } return 1; } - -static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, +/* Return < 0 if no scan results could be fetched. */ +static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_bss *selected; @@ -885,32 +1012,70 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_supplicant_notify_scanning(wpa_s, 0); +#ifdef CONFIG_P2P + if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "stopped scan processing"); + return -1; + } + } +#endif /* CONFIG_P2P */ + scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); if (scan_res == NULL) { if (wpa_s->conf->ap_scan == 2 || ap) - return; - wpa_printf(MSG_DEBUG, "Failed to get scan results - try " - "scanning again"); + return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " + "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); - return; + return -1; } +#ifndef CONFIG_NO_RANDOM_POOL + size_t i, num; + num = scan_res->num; + if (num > 10) + num = 10; + for (i = 0; i < num; i++) { + u8 buf[5]; + struct wpa_scan_res *res = scan_res->res[i]; + buf[0] = res->bssid[5]; + buf[1] = res->qual & 0xff; + buf[2] = res->noise & 0xff; + buf[3] = res->level & 0xff; + buf[4] = res->tsf & 0xff; + random_add_randomness(buf, sizeof(buf)); + } +#endif /* CONFIG_NO_RANDOM_POOL */ + if (wpa_s->scan_res_handler) { - wpa_s->scan_res_handler(wpa_s, scan_res); + void (*scan_res_handler)(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + + scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; + scan_res_handler(wpa_s, scan_res); + wpa_scan_results_free(scan_res); - return; + return 0; } if (ap) { - wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode"); + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode"); +#ifdef CONFIG_AP + if (wpa_s->ap_iface->scan_cb) + wpa_s->ap_iface->scan_cb(wpa_s->ap_iface); +#endif /* CONFIG_AP */ wpa_scan_results_free(scan_res); - return; + return 0; } - wpa_printf(MSG_DEBUG, "New scan results available"); + wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available"); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); wpas_notify_scan_results(wpa_s); @@ -918,22 +1083,21 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { wpa_scan_results_free(scan_res); - return; + return 0; } if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); wpa_scan_results_free(scan_res); - return; + return 0; } - if (bgscan_notify_scan(wpa_s) == 1) { + if (!wpas_driver_bss_selection(wpa_s) && + bgscan_notify_scan(wpa_s, scan_res) == 1) { wpa_scan_results_free(scan_res); - return; + return 0; } - wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res); - selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); if (selected) { @@ -941,24 +1105,91 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid, scan_res); wpa_scan_results_free(scan_res); - if (skip) - return; - wpa_supplicant_connect(wpa_s, selected, ssid); + if (skip) { + wpa_supplicant_rsn_preauth_scan_results(wpa_s); + return 0; + } + + if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); + return -1; + } + wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else { wpa_scan_results_free(scan_res); - wpa_printf(MSG_DEBUG, "No suitable network found"); + wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { - wpa_printf(MSG_DEBUG, "Setup a new network"); + wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network"); wpa_supplicant_associate(wpa_s, NULL, ssid); + wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else { - int timeout_sec = 5; + int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; - wpa_supplicant_req_new_scan(wpa_s, timeout_sec, - timeout_usec); +#ifdef CONFIG_P2P + if (wpa_s->p2p_in_provisioning) { + /* + * Use shorter wait during P2P Provisioning + * state to speed up group formation. + */ + timeout_sec = 0; + timeout_usec = 250000; + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + return 0; + } +#endif /* CONFIG_P2P */ + if (wpa_supplicant_req_sched_scan(wpa_s)) + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); } } + return 0; } + + +static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + + if (_wpa_supplicant_event_scan_results(wpa_s, data) < 0) { + /* + * If no scan results could be fetched, then no need to + * notify those interfaces that did not actually request + * this scan. + */ + return; + } + + /* + * Check other interfaces to see if they have the same radio-name. If + * so, they get updated with this same scan info. + */ + if (!wpa_s->driver->get_radio_name) + return; + + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces " + "sharing same radio (%s) in event_scan_results", rn); + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (rn2 && os_strcmp(rn, rn2) == 0) { + wpa_printf(MSG_DEBUG, "%s: Updating scan results from " + "sibling", ifs->ifname); + _wpa_supplicant_event_scan_results(ifs, data); + } + } +} + #endif /* CONFIG_NO_SCAN_PROCESSING */ @@ -968,19 +1199,25 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, int l, len, found = 0, wpa_found, rsn_found; const u8 *p; - wpa_printf(MSG_DEBUG, "Association info event"); + wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); if (data->assoc_info.req_ies) wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, data->assoc_info.req_ies_len); - if (data->assoc_info.resp_ies) + if (data->assoc_info.resp_ies) { wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); +#ifdef CONFIG_TDLS + wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_TDLS */ + } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", data->assoc_info.beacon_ies, data->assoc_info.beacon_ies_len); if (data->assoc_info.freq) - wpa_printf(MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); + wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", + data->assoc_info.freq); p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; @@ -1017,8 +1254,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, bssid) < 0) { - wpa_printf(MSG_DEBUG, "FT: Validation of " - "Reassociation Response failed"); + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " + "Reassociation Response failed"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_INVALID_IE); return -1; @@ -1028,6 +1265,27 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, p = data->assoc_info.resp_ies; l = data->assoc_info.resp_ies_len; +#ifdef CONFIG_WPS_STRICT + if (p && wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE); + if (wps == NULL) { + wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not " + "include WPS IE in (Re)Association Response"); + return -1; + } + + if (wps_validate_assoc_resp(wps) < 0) { + wpabuf_free(wps); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + wpabuf_free(wps); + } +#endif /* CONFIG_WPS_STRICT */ + /* Go through the IEs and make a copy of the MDIE, if present. */ while (p && l >= 2) { len = p[1] + 2; @@ -1090,6 +1348,14 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; + if (wpa_s->assoc_freq && data->assoc_info.freq && + wpa_s->assoc_freq != data->assoc_info.freq) { + wpa_printf(MSG_DEBUG, "Operating frequency changed from " + "%u to %u MHz", + wpa_s->assoc_freq, data->assoc_info.freq); + wpa_supplicant_update_scan_results(wpa_s); + } + wpa_s->assoc_freq = data->assoc_info.freq; return 0; @@ -1109,7 +1375,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, - data->assoc_info.req_ies_len); + data->assoc_info.req_ies_len, + data->assoc_info.reassoc); return; } #endif /* CONFIG_AP */ @@ -1119,13 +1386,11 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, return; wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || - (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && - os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) { - wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" + if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); + random_add_randomness(bssid, ETH_ALEN); bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); @@ -1209,6 +1474,23 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + /* + * The driver will take care of RSN 4-way handshake, so we need + * to allow EAPOL supplicant to complete its work without + * waiting for WPA supplicant. + */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + } else if (ft_completed) { + /* + * FT protocol completed - make sure EAPOL state machine ends + * up in authenticated. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } if (wpa_s->pending_eapol_rx) { @@ -1218,9 +1500,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (age.sec == 0 && age.usec < 100000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "Process pending EAPOL frame " - "that was received just before association " - "notification"); + wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL " + "frame that was received just before " + "association notification"); wpa_supplicant_rx_eapol( wpa_s, wpa_s->pending_eapol_rx_src, wpabuf_head(wpa_s->pending_eapol_rx), @@ -1230,25 +1512,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s->pending_eapol_rx = NULL; } -#ifdef CONFIG_BGSCAN - if (wpa_s->current_ssid != wpa_s->bgscan_ssid) { - bgscan_deinit(wpa_s); - if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { - if (bgscan_init(wpa_s, wpa_s->current_ssid)) { - wpa_printf(MSG_DEBUG, "Failed to initialize " - "bgscan"); - /* - * Live without bgscan; it is only used as a - * roaming optimization, so the initial - * connection is not affected. - */ - } else - wpa_s->bgscan_ssid = wpa_s->current_ssid; - } else - wpa_s->bgscan_ssid = NULL; - } -#endif /* CONFIG_BGSCAN */ - if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 && @@ -1256,6 +1519,24 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); } + +#ifdef CONFIG_IBSS_RSN + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_IBSS && + wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && + wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE && + wpa_s->ibss_rsn == NULL) { + wpa_s->ibss_rsn = ibss_rsn_init(wpa_s); + if (!wpa_s->ibss_rsn) { + wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + + ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); + } +#endif /* CONFIG_IBSS_RSN */ } @@ -1263,13 +1544,11 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, u16 reason_code) { const u8 *bssid; -#ifdef CONFIG_SME int authenticating; u8 prev_pending_bssid[ETH_ALEN]; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); -#endif /* CONFIG_SME */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* @@ -1277,8 +1556,8 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, * generating streams of disconnected events when configuring * IBSS for WPA-None. Ignore them for now. */ - wpa_printf(MSG_DEBUG, "Disconnect event - ignore in " - "IBSS/WPA-None mode"); + wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in " + "IBSS/WPA-None mode"); return; } @@ -1287,51 +1566,49 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); } - if (wpa_s->wpa_state >= WPA_ASSOCIATED) - wpa_supplicant_req_scan(wpa_s, 0, 100000); + if (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to " + "reconnect (wps=%d)", + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); + if (wpa_s->wpa_state >= WPA_ASSOCIATING) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not " + "try to re-connect"); + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); + } bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; - wpa_blacklist_add(wpa_s, bssid); + wpas_connection_failed(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR " reason=%d", MAC2STR(bssid), reason_code); if (wpa_supplicant_dynamic_keys(wpa_s)) { - wpa_printf(MSG_DEBUG, "Disconnect event - remove keys"); + wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); wpa_s->keys_cleared = 0; wpa_clear_keys(wpa_s, wpa_s->bssid); } wpa_supplicant_mark_disassoc(wpa_s); - bgscan_deinit(wpa_s); - wpa_s->bgscan_ssid = NULL; -#ifdef CONFIG_SME - if (authenticating && - (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { - /* - * mac80211-workaround to force deauth on failed auth cmd, - * requires us to remain in authenticating state to allow the - * second authentication attempt to be continued properly. - */ - wpa_printf(MSG_DEBUG, "SME: Allow pending authentication to " - "proceed after disconnection event"); - wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); - os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN); - } -#endif /* CONFIG_SME */ + + if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); } #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT -static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, - void *sock_ctx) +void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; if (!wpa_s->pending_mic_error_report) return; - wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report"); wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); wpa_s->pending_mic_error_report = 0; } @@ -1406,8 +1683,8 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, sec = os_random() % 60; else sec = WPA_GET_BE32(rval) % 60; - wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d " - "seconds", sec); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error " + "report %d seconds", sec); wpa_s->pending_mic_error_report = 1; wpa_s->pending_mic_error_pairwise = pairwise; eloop_cancel_timeout( @@ -1454,18 +1731,24 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, if (!wpa_s->interface_removed) break; wpa_s->interface_removed = 0; - wpa_printf(MSG_DEBUG, "Configured interface was added."); + wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added"); if (wpa_supplicant_driver_init(wpa_s) < 0) { - wpa_printf(MSG_INFO, "Failed to initialize the driver " - "after interface was added."); + wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " + "driver after interface was added"); } + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); break; case EVENT_INTERFACE_REMOVED: - wpa_printf(MSG_DEBUG, "Configured interface was removed."); + wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); wpa_s->interface_removed = 1; wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); l2_packet_deinit(wpa_s->l2); wpa_s->l2 = NULL; +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) @@ -1488,6 +1771,25 @@ wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s, #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS +static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + switch (data->tdls.oper) { + case TDLS_REQUEST_SETUP: + wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + break; + case TDLS_REQUEST_TEARDOWN: + wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer, + data->tdls.reason_code); + break; + } +} +#endif /* CONFIG_TDLS */ + + #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, @@ -1512,8 +1814,17 @@ wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { + struct wpa_ssid *ssid; + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return; if (data == NULL) return; + ssid = wpa_s->current_ssid; + if (ssid == NULL) + return; + if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) + return; + ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); } #endif /* CONFIG_IBSS_RSN */ @@ -1536,19 +1847,19 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, sta_addr = data + 1; target_ap_addr = data + 1 + ETH_ALEN; status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN); - wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR - " TargetAP " MACSTR " status %u", - MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA " + MACSTR " TargetAP " MACSTR " status %u", + MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR - " in FT Action Response", MAC2STR(sta_addr)); + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR + " in FT Action Response", MAC2STR(sta_addr)); return; } if (status) { - wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates " - "failure (status code %d)", status); + wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates " + "failure (status code %d)", status); /* TODO: report error to FT code(?) */ return; } @@ -1573,12 +1884,94 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, #endif /* CONFIG_IEEE80211R */ +static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s, + struct unprot_deauth *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, + struct unprot_disassoc *e) +{ +#ifdef CONFIG_IEEE80211W + wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame " + "dropped: " MACSTR " -> " MACSTR + " (reason code %u)", + MAC2STR(e->sa), MAC2STR(e->da), e->reason_code); + sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code); +#endif /* CONFIG_IEEE80211W */ +} + + +static void wnm_action_rx(struct wpa_supplicant *wpa_s, struct rx_action *rx) +{ + u8 action, mode; + const u8 *pos, *end; + + if (rx->data == NULL || rx->len == 0) + return; + + pos = rx->data; + end = pos + rx->len; + action = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, + action, MAC2STR(rx->sa)); + switch (action) { + case WNM_BSS_TRANS_MGMT_REQ: + if (pos + 5 > end) + break; + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " + "Request: dialog_token=%u request_mode=0x%x " + "disassoc_timer=%u validity_interval=%u", + pos[0], pos[1], WPA_GET_LE16(pos + 2), pos[4]); + mode = pos[1]; + pos += 5; + if (mode & 0x08) + pos += 12; /* BSS Termination Duration */ + if (mode & 0x10) { + char url[256]; + if (pos + 1 > end || pos + 1 + pos[0] > end) { + wpa_printf(MSG_DEBUG, "WNM: Invalid BSS " + "Transition Management Request " + "(URL)"); + break; + } + os_memcpy(url, pos + 1, pos[0]); + url[pos[0]] = '\0'; + wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation " + "Imminent - session_info_url=%s", url); + } + break; + } +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; u16 reason_code = 0; + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && + event != EVENT_INTERFACE_ENABLED && + event != EVENT_INTERFACE_STATUS) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore event %s (%d) while interface is disabled", + event_to_string(event), event); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Event %s (%d) received", + event_to_string(event), event); + switch (event) { case EVENT_AUTH: sme_event_auth(wpa_s, data); @@ -1587,24 +1980,67 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_supplicant_event_assoc(wpa_s, data); break; case EVENT_DISASSOC: - wpa_printf(MSG_DEBUG, "Disassociation notification"); + wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); + if (data) { + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", + data->disassoc_info.reason_code); + if (data->disassoc_info.addr) + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(data->disassoc_info.addr)); + } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->disassoc_info.addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], data->disassoc_info.addr); break; } + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in " + "AP mode"); + break; + } #endif /* CONFIG_AP */ - if (data) - reason_code = data->deauth_info.reason_code; + if (data) { + reason_code = data->disassoc_info.reason_code; + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_disassoc_notif( + wpa_s, data->disassoc_info.addr, reason_code, + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#endif /* CONFIG_P2P */ + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_disassoc(wpa_s, data); /* fall through */ case EVENT_DEAUTH: if (event == EVENT_DEAUTH) { - wpa_printf(MSG_DEBUG, "Deauthentication notification"); - if (data) + wpa_dbg(wpa_s, MSG_DEBUG, + "Deauthentication notification"); + if (data) { reason_code = data->deauth_info.reason_code; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", + data->deauth_info.reason_code); + if (data->deauth_info.addr) { + wpa_dbg(wpa_s, MSG_DEBUG, " * address " + MACSTR, + MAC2STR(data->deauth_info. + addr)); + } + wpa_hexdump(MSG_DEBUG, + "Deauthentication frame IE(s)", + data->deauth_info.ie, + data->deauth_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_deauth_notif( + wpa_s, data->deauth_info.addr, + reason_code, + data->deauth_info.ie, + data->deauth_info.ie_len); +#endif /* CONFIG_P2P */ + } } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->deauth_info.addr) { @@ -1612,6 +2048,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->deauth_info.addr); break; } + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in " + "AP mode"); + break; + } #endif /* CONFIG_AP */ wpa_supplicant_event_disassoc(wpa_s, reason_code); break; @@ -1637,6 +2078,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_supplicant_event_stkstart(wpa_s, data); break; #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + case EVENT_TDLS: + wpa_supplicant_event_tdls(wpa_s, data); + break; +#endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); @@ -1648,18 +2094,69 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* CONFIG_IBSS_RSN */ case EVENT_ASSOC_REJECT: - sme_event_assoc_reject(wpa_s, data); + if (data->assoc_reject.bssid) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "bssid=" MACSTR " status_code=%u", + MAC2STR(data->assoc_reject.bssid), + data->assoc_reject.status_code); + else + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "status_code=%u", + data->assoc_reject.status_code); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_assoc_reject(wpa_s, data); break; case EVENT_AUTH_TIMED_OUT: - sme_event_auth_timed_out(wpa_s, data); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: - sme_event_assoc_timed_out(wpa_s, data); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_assoc_timed_out(wpa_s, data); break; -#ifdef CONFIG_AP case EVENT_TX_STATUS: - if (wpa_s->ap_iface == NULL) + wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR + " type=%d stype=%d", + MAC2STR(data->tx_status.dst), + data->tx_status.type, data->tx_status.stype); +#ifdef CONFIG_AP + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_OFFCHANNEL + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION) + offchannel_send_action_tx_status( + wpa_s, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack ? + OFFCHANNEL_SEND_ACTION_SUCCESS : + OFFCHANNEL_SEND_ACTION_NO_ACK); +#endif /* CONFIG_OFFCHANNEL */ break; + } +#endif /* CONFIG_AP */ +#ifdef CONFIG_OFFCHANNEL + wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" + MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); + /* + * Catch TX status events for Action frames we sent via group + * interface in GO mode. + */ + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION && + os_memcmp(wpa_s->parent->pending_action_dst, + data->tx_status.dst, ETH_ALEN) == 0) { + offchannel_send_action_tx_status( + wpa_s->parent, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack ? + OFFCHANNEL_SEND_ACTION_SUCCESS : + OFFCHANNEL_SEND_ACTION_NO_ACK); + break; + } +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_AP switch (data->tx_status.type) { case WLAN_FC_TYPE_MGMT: ap_mgmt_tx_cb(wpa_s, data->tx_status.data, @@ -1674,25 +2171,52 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->tx_status.ack); break; } +#endif /* CONFIG_AP */ + break; +#ifdef CONFIG_AP + case EVENT_DRIVER_CLIENT_POLL_OK: + ap_client_poll_ok(wpa_s, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: if (wpa_s->ap_iface == NULL) break; - ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame, - data->rx_from_unknown.len); + ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, + data->rx_from_unknown.wds); break; case EVENT_RX_MGMT: - if (wpa_s->ap_iface == NULL) + if (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + u16 fc, stype; + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) + data->rx_mgmt.frame; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + const u8 *src = mgmt->sa; + const u8 *ie = mgmt->u.probe_req.variable; + size_t ie_len = data->rx_mgmt.frame_len - + (mgmt->u.probe_req.variable - + data->rx_mgmt.frame); + wpas_p2p_probe_req_rx(wpa_s, src, mgmt->da, + mgmt->bssid, ie, ie_len); + break; + } +#endif /* CONFIG_P2P */ + wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " + "management frame in non-AP mode"); break; + } ap_mgmt_rx(wpa_s, &data->rx_mgmt); break; #endif /* CONFIG_AP */ case EVENT_RX_ACTION: - wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR - " Category=%u DataLen=%d freq=%d MHz", - MAC2STR(data->rx_action.sa), - data->rx_action.category, (int) data->rx_action.len, - data->rx_action.freq); + wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR + " Category=%u DataLen=%d freq=%d MHz", + MAC2STR(data->rx_action.sa), + data->rx_action.category, (int) data->rx_action.len, + data->rx_action.freq); #ifdef CONFIG_IEEE80211R if (data->rx_action.category == WLAN_ACTION_FT) { ft_rx_action(wpa_s, data->rx_action.data, @@ -1700,19 +2224,157 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_SME + if (data->rx_action.category == WLAN_ACTION_SA_QUERY) { + sme_sa_query_rx(wpa_s, data->rx_action.sa, + data->rx_action.data, + data->rx_action.len); + break; + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_GAS + if (data->rx_action.category == WLAN_ACTION_PUBLIC && + gas_query_rx(wpa_s->gas, data->rx_action.da, + data->rx_action.sa, data->rx_action.bssid, + data->rx_action.data, data->rx_action.len, + data->rx_action.freq) == 0) + break; +#endif /* CONFIG_GAS */ + if (data->rx_action.category == WLAN_ACTION_WNM) { + wnm_action_rx(wpa_s, &data->rx_action); + break; + } +#ifdef CONFIG_TDLS + if (data->rx_action.category == WLAN_ACTION_PUBLIC && + data->rx_action.len >= 4 && + data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { + wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery " + "Response from " MACSTR, + MAC2STR(data->rx_action.sa)); + break; + } +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_P2P + wpas_p2p_rx_action(wpa_s, data->rx_action.da, + data->rx_action.sa, + data->rx_action.bssid, + data->rx_action.category, + data->rx_action.data, + data->rx_action.len, data->rx_action.freq); +#endif /* CONFIG_P2P */ break; -#ifdef CONFIG_CLIENT_MLME - case EVENT_MLME_RX: { - struct ieee80211_rx_status rx_status; - os_memset(&rx_status, 0, sizeof(rx_status)); - rx_status.freq = data->mlme_rx.freq; - rx_status.channel = data->mlme_rx.channel; - rx_status.ssi = data->mlme_rx.ssi; - ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len, - &rx_status); + case EVENT_RX_PROBE_REQ: + if (data->rx_probe_req.sa == NULL || + data->rx_probe_req.ie == NULL) + break; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + hostapd_probe_req_rx(wpa_s->ap_iface->bss[0], + data->rx_probe_req.sa, + data->rx_probe_req.da, + data->rx_probe_req.bssid, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); + break; + } +#endif /* CONFIG_AP */ +#ifdef CONFIG_P2P + wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, + data->rx_probe_req.da, + data->rx_probe_req.bssid, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); +#endif /* CONFIG_P2P */ break; - } -#endif /* CONFIG_CLIENT_MLME */ + case EVENT_REMAIN_ON_CHANNEL: +#ifdef CONFIG_OFFCHANNEL + offchannel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq, + data->remain_on_channel.duration); +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_P2P + wpas_p2p_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq, + data->remain_on_channel.duration); +#endif /* CONFIG_P2P */ + break; + case EVENT_CANCEL_REMAIN_ON_CHANNEL: +#ifdef CONFIG_OFFCHANNEL + offchannel_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_OFFCHANNEL */ +#ifdef CONFIG_P2P + wpas_p2p_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_P2P */ + break; +#ifdef CONFIG_P2P + case EVENT_P2P_DEV_FOUND: { + struct p2p_peer_info peer_info; + + os_memset(&peer_info, 0, sizeof(peer_info)); + if (data->p2p_dev_found.dev_addr) + os_memcpy(peer_info.p2p_device_addr, + data->p2p_dev_found.dev_addr, ETH_ALEN); + if (data->p2p_dev_found.pri_dev_type) + os_memcpy(peer_info.pri_dev_type, + data->p2p_dev_found.pri_dev_type, + sizeof(peer_info.pri_dev_type)); + if (data->p2p_dev_found.dev_name) + os_strlcpy(peer_info.device_name, + data->p2p_dev_found.dev_name, + sizeof(peer_info.device_name)); + peer_info.config_methods = data->p2p_dev_found.config_methods; + peer_info.dev_capab = data->p2p_dev_found.dev_capab; + peer_info.group_capab = data->p2p_dev_found.group_capab; + + /* + * FIX: new_device=1 is not necessarily correct. We should + * maintain a P2P peer database in wpa_supplicant and update + * this information based on whether the peer is truly new. + */ + wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1); + break; + } + case EVENT_P2P_GO_NEG_REQ_RX: + wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src, + data->p2p_go_neg_req_rx.dev_passwd_id); + break; + case EVENT_P2P_GO_NEG_COMPLETED: + wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res); + break; + case EVENT_P2P_PROV_DISC_REQUEST: + wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer, + data->p2p_prov_disc_req.config_methods, + data->p2p_prov_disc_req.dev_addr, + data->p2p_prov_disc_req.pri_dev_type, + data->p2p_prov_disc_req.dev_name, + data->p2p_prov_disc_req.supp_config_methods, + data->p2p_prov_disc_req.dev_capab, + data->p2p_prov_disc_req.group_capab, + NULL, 0); + break; + case EVENT_P2P_PROV_DISC_RESPONSE: + wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer, + data->p2p_prov_disc_resp.config_methods); + break; + case EVENT_P2P_SD_REQUEST: + wpas_sd_request(wpa_s, data->p2p_sd_req.freq, + data->p2p_sd_req.sa, + data->p2p_sd_req.dialog_token, + data->p2p_sd_req.update_indic, + data->p2p_sd_req.tlvs, + data->p2p_sd_req.tlvs_len); + break; + case EVENT_P2P_SD_RESPONSE: + wpas_sd_response(wpa_s, data->p2p_sd_resp.sa, + data->p2p_sd_resp.update_indic, + data->p2p_sd_resp.tlvs, + data->p2p_sd_resp.tlvs_len); + break; +#endif /* CONFIG_P2P */ case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, data->eapol_rx.data, @@ -1720,10 +2382,116 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_SIGNAL_CHANGE: bgscan_notify_signal_change( - wpa_s, data->signal_change.above_threshold); + wpa_s, data->signal_change.above_threshold, + data->signal_change.current_signal, + data->signal_change.current_noise, + data->signal_change.current_txrate); + break; + case EVENT_INTERFACE_ENABLED: + wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_supplicant_update_mac_addr(wpa_s); +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) { + wpa_supplicant_set_state(wpa_s, + WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else + wpa_supplicant_set_state(wpa_s, + WPA_COMPLETED); +#else /* CONFIG_AP */ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_supplicant_req_scan(wpa_s, 0, 0); +#endif /* CONFIG_AP */ + } + break; + case EVENT_INTERFACE_DISABLED: + wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); + break; + case EVENT_CHANNEL_LIST_CHANGED: + if (wpa_s->drv_priv == NULL) + break; /* Ignore event during drv initialization */ + + free_hw_features(wpa_s); + wpa_s->hw.modes = wpa_drv_get_hw_feature_data( + wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); + +#ifdef CONFIG_P2P + wpas_p2p_update_channel_list(wpa_s); +#endif /* CONFIG_P2P */ + break; + case EVENT_INTERFACE_UNAVAILABLE: +#ifdef CONFIG_P2P + wpas_p2p_interface_unavailable(wpa_s); +#endif /* CONFIG_P2P */ + break; + case EVENT_BEST_CHANNEL: + wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received " + "(%d %d %d)", + data->best_chan.freq_24, data->best_chan.freq_5, + data->best_chan.freq_overall); + wpa_s->best_24_freq = data->best_chan.freq_24; + wpa_s->best_5_freq = data->best_chan.freq_5; + wpa_s->best_overall_freq = data->best_chan.freq_overall; +#ifdef CONFIG_P2P + wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24, + data->best_chan.freq_5, + data->best_chan.freq_overall); +#endif /* CONFIG_P2P */ + break; + case EVENT_UNPROT_DEAUTH: + wpa_supplicant_event_unprot_deauth(wpa_s, + &data->unprot_deauth); + break; + case EVENT_UNPROT_DISASSOC: + wpa_supplicant_event_unprot_disassoc(wpa_s, + &data->unprot_disassoc); + break; + case EVENT_STATION_LOW_ACK: +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data) + hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0], + data->low_ack.addr); +#endif /* CONFIG_AP */ +#ifdef CONFIG_TDLS + if (data) + wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr); +#endif /* CONFIG_TDLS */ + break; + case EVENT_IBSS_PEER_LOST: +#ifdef CONFIG_IBSS_RSN + ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer); +#endif /* CONFIG_IBSS_RSN */ + break; + case EVENT_DRIVER_GTK_REKEY: + if (os_memcmp(data->driver_gtk_rekey.bssid, + wpa_s->bssid, ETH_ALEN)) + break; + if (!wpa_s->wpa) + break; + wpa_sm_update_replay_ctr(wpa_s->wpa, + data->driver_gtk_rekey.replay_ctr); + break; + case EVENT_SCHED_SCAN_STOPPED: + wpa_s->sched_scanning = 0; + wpa_supplicant_notify_scanning(wpa_s, 0); + + /* + * If we timed out, start a new sched scan to continue + * searching for more SSIDs. + */ + if (wpa_s->sched_scan_timed_out) + wpa_supplicant_req_sched_scan(wpa_s); + break; + case EVENT_WPS_BUTTON_PUSHED: +#ifdef CONFIG_WPS + wpas_wps_start_pbc(wpa_s, NULL, 0); +#endif /* CONFIG_WPS */ break; default: - wpa_printf(MSG_INFO, "Unknown event %d", event); + wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } diff --git a/wpa_supplicant/examples/p2p-action-udhcp.sh b/wpa_supplicant/examples/p2p-action-udhcp.sh new file mode 100644 index 0000000..d7d0e79 --- /dev/null +++ b/wpa_supplicant/examples/p2p-action-udhcp.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +kill_daemon() { + NAME=$1 + PF=$2 + + if [ ! -r $PF ]; then + return + fi + + PID=`cat $PF` + if [ $PID -gt 0 ]; then + if ps $PID | grep -q $NAME; then + kill $PID + fi + fi + rm $PF +} + +if [ "$CMD" = "P2P-GROUP-STARTED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 192.168.42.1 up + udhcpd /etc/udhcpd-p2p.conf + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \ + -s /etc/udhcpc.script + fi +fi + +if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi + if [ "$4" = "client" ]; then + kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid + ifconfig $GIFNAME 0.0.0.0 + fi +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # enable NAT/masquarade $GIFNAME -> $UPLINK + iptables -P FORWARD DROP + iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE + iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT + sysctl net.ipv4.ip_forward=1 +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # disable NAT/masquarade $GIFNAME -> $UPLINK + sysctl net.ipv4.ip_forward=0 + iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE + iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT +fi diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh new file mode 100644 index 0000000..8759f54 --- /dev/null +++ b/wpa_supplicant/examples/p2p-action.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +IFNAME=$1 +CMD=$2 + +kill_daemon() { + NAME=$1 + PF=$2 + + if [ ! -r $PF ]; then + return + fi + + PID=`cat $PF` + if [ $PID -gt 0 ]; then + if ps $PID | grep -q $NAME; then + kill $PID + fi + fi + rm $PF +} + +if [ "$CMD" = "P2P-GROUP-STARTED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid + rm /var/run/dhclient.leases-$GIFNAME + kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + ifconfig $GIFNAME 192.168.42.1 up + if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \ + -i $GIFNAME \ + -F192.168.42.11,192.168.42.99; then + # another dnsmasq instance may be running and blocking us; try to + # start with -z to avoid that + dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \ + -i $GIFNAME \ + -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z + fi + fi + if [ "$4" = "client" ]; then + kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid + rm /var/run/dhclient.leases-$GIFNAME + kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + dhclient -pf /var/run/dhclient-$GIFNAME.pid \ + -lf /var/run/dhclient.leases-$GIFNAME \ + -nw \ + $GIFNAME + fi +fi + +if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then + GIFNAME=$3 + if [ "$4" = "GO" ]; then + kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + ifconfig $GIFNAME 0.0.0.0 + fi + if [ "$4" = "client" ]; then + kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid + rm /var/run/dhclient.leases-$GIFNAME + ifconfig $GIFNAME 0.0.0.0 + fi +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # enable NAT/masquarade $GIFNAME -> $UPLINK + iptables -P FORWARD DROP + iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE + iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT + sysctl net.ipv4.ip_forward=1 +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # disable NAT/masquarade $GIFNAME -> $UPLINK + sysctl net.ipv4.ip_forward=0 + iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE + iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT +fi diff --git a/wpa_supplicant/examples/udhcpd-p2p.conf b/wpa_supplicant/examples/udhcpd-p2p.conf new file mode 100644 index 0000000..df59094 --- /dev/null +++ b/wpa_supplicant/examples/udhcpd-p2p.conf @@ -0,0 +1,120 @@ +# Sample udhcpd configuration file (/etc/udhcpd.conf) + +# The start and end of the IP lease block + +start 192.168.42.20 #default: 192.168.0.20 +end 192.168.42.254 #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface wlan2 #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFER's, DECLINE's, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the leases file + +#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +pidfile /var/run/udhcpd-wlan2.pid #default: /var/run/udhcpd.pid + +# Every time udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- useful for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword 'opt' or 'option'. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is 'lease'. + +#Examles +opt dns 192.168.2.1 +option subnet 255.255.255.0 +option domain atherosowl.com +option lease 864000 # 10 days of seconds + + +# Currently supported options, for more info, see options.c +#opt subnet +#opt timezone +#opt router +#opt timesvr +#opt namesvr +#opt dns +#opt logsvr +#opt cookiesvr +#opt lprsvr +#opt bootsize +#opt domain +#opt swapsvr +#opt rootpath +#opt ipttl +#opt mtu +#opt broadcast +#opt wins +#opt lease +#opt ntpsrv +#opt tftp +#opt bootfile + + +# Static leases map +#static_lease 00:60:08:11:CE:4E 192.168.0.54 +#static_lease 00:60:08:11:CE:3E 192.168.0.44 + + diff --git a/wpa_supplicant/examples/wpas-dbus-new-signals.py b/wpa_supplicant/examples/wpas-dbus-new-signals.py index b040e0a..d90ef18 100755 --- a/wpa_supplicant/examples/wpas-dbus-new-signals.py +++ b/wpa_supplicant/examples/wpas-dbus-new-signals.py @@ -59,12 +59,12 @@ def showBss(bss): dbus_interface=dbus.PROPERTIES_IFACE) ssid = byte_array_to_string(val) - val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE', + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA', dbus_interface=dbus.PROPERTIES_IFACE) wpa = "no" if val != None: wpa = "yes" - val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE', + val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN', dbus_interface=dbus.PROPERTIES_IFACE) wpa2 = "no" if val != None: diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c new file mode 100644 index 0000000..3b736da --- /dev/null +++ b/wpa_supplicant/gas_query.c @@ -0,0 +1,481 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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 "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "offchannel.h" +#include "gas_query.h" + + +#define GAS_QUERY_TIMEOUT 5 + + +struct gas_query_pending { + struct dl_list list; + u8 addr[ETH_ALEN]; + u8 dialog_token; + u8 next_frag_id; + unsigned int wait_comeback:1; + unsigned int offchannel_tx_started:1; + int freq; + u16 status_code; + struct wpabuf *adv_proto; + struct wpabuf *resp; + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code); + void *ctx; +}; + +struct gas_query { + struct wpa_supplicant *wpa_s; + struct dl_list pending; /* struct gas_query_pending */ +}; + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_timeout(void *eloop_data, void *user_ctx); + + +struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s) +{ + struct gas_query *gas; + + gas = os_zalloc(sizeof(*gas)); + if (gas == NULL) + return NULL; + + gas->wpa_s = wpa_s; + dl_list_init(&gas->pending); + + return gas; +} + + +static void gas_query_done(struct gas_query *gas, + struct gas_query_pending *query, + enum gas_query_result result) +{ + if (query->offchannel_tx_started) + offchannel_send_action_done(gas->wpa_s); + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + dl_list_del(&query->list); + query->cb(query->ctx, query->addr, query->dialog_token, result, + query->adv_proto, query->resp, query->status_code); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + +void gas_query_deinit(struct gas_query *gas) +{ + struct gas_query_pending *query, *next; + + if (gas == NULL) + return; + + dl_list_for_each_safe(query, next, &gas->pending, + struct gas_query_pending, list) + gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT); + + os_free(gas); +} + + +static struct gas_query_pending * +gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 && + q->dialog_token == dialog_token) + return q; + } + return NULL; +} + + +static int gas_query_append(struct gas_query_pending *query, const u8 *data, + size_t len) +{ + if (wpabuf_resize(&query->resp, len) < 0) { + wpa_printf(MSG_DEBUG, "GAS: No memory to store the response"); + return -1; + } + wpabuf_put_data(query->resp, data, len); + return 0; +} + + +static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, + struct wpabuf *req) +{ + int res; + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " + "freq=%d", MAC2STR(query->addr), + (unsigned int) wpabuf_len(req), query->freq); + res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, + gas->wpa_s->own_addr, query->addr, + wpabuf_head(req), wpabuf_len(req), 1000, + NULL, 0); + if (res == 0) + query->offchannel_tx_started = 1; + return res; +} + + +static void gas_query_tx_comeback_req(struct gas_query *gas, + struct gas_query_pending *query) +{ + struct wpabuf *req; + + req = gas_build_comeback_req(query->dialog_token); + if (req == NULL) { + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + return; + } + + if (gas_query_tx(gas, query, req) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + } + + wpabuf_free(req); +} + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR, + MAC2STR(query->addr)); + gas_query_tx_comeback_req(gas, query); +} + + +static void gas_query_tx_comeback_req_delay(struct gas_query *gas, + struct gas_query_pending *query, + u16 comeback_delay) +{ + unsigned int secs, usecs; + + secs = (comeback_delay * 1024) / 1000000; + usecs = comeback_delay * 1024 - secs * 1000000; + wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR + " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs); + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout, + gas, query); +} + + +static void gas_query_rx_initial(struct gas_query *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received initial response from " + MACSTR " (dialog_token=%u comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, comeback_delay); + + query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]); + if (query->adv_proto == NULL) { + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + return; + } + + if (comeback_delay) { + query->wait_comeback = 1; + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + /* Query was completed without comeback mechanism */ + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + return; + } + + gas_query_done(gas, query, GAS_QUERY_SUCCESS); +} + + +static void gas_query_rx_comeback(struct gas_query *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u8 frag_id, u8 more_frags, + u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received comeback response from " + MACSTR " (dialog_token=%u frag_id=%u more_frags=%u " + "comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, frag_id, + more_frags, comeback_delay); + + if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || + os_memcmp(adv_proto, wpabuf_head(query->adv_proto), + wpabuf_len(query->adv_proto)) != 0) { + wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed " + "between initial and comeback response from " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_PEER_ERROR); + return; + } + + if (comeback_delay) { + if (frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response " + "with non-zero frag_id and comeback_delay " + "from " MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_PEER_ERROR); + return; + } + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + if (frag_id != query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " + "from " MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_PEER_ERROR); + return; + } + query->next_frag_id++; + + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); + return; + } + + if (more_frags) { + gas_query_tx_comeback_req(gas, query); + return; + } + + gas_query_done(gas, query, GAS_QUERY_SUCCESS); +} + + +int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, + const u8 *bssid, const u8 *data, size_t len, int freq) +{ + struct gas_query_pending *query; + u8 action, dialog_token, frag_id = 0, more_frags = 0; + u16 comeback_delay, resp_len; + const u8 *pos, *adv_proto; + + if (gas == NULL || len < 4) + return -1; + + pos = data; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_RESP && + action != WLAN_PA_GAS_COMEBACK_RESP) + return -1; /* Not a GAS response */ + + query = gas_query_get_pending(gas, sa, dialog_token); + if (query == NULL) { + wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR + " dialog token %u", MAC2STR(sa), dialog_token); + return -1; + } + + if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from " + MACSTR " dialog token %u when waiting for comeback " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from " + MACSTR " dialog token %u when waiting for initial " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + query->status_code = WPA_GET_LE16(pos); + pos += 2; + + if (query->status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token " + "%u failed - status code %u", + MAC2STR(sa), dialog_token, query->status_code); + gas_query_done(gas, query, GAS_QUERY_FAILURE); + return 0; + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) { + if (pos + 1 > data + len) + return 0; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + } + + /* Comeback Delay */ + if (pos + 2 > data + len) + return 0; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + + /* Advertisement Protocol element */ + if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement " + "Protocol element in the response from " MACSTR, + MAC2STR(sa)); + return 0; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement " + "Protocol element ID %u in response from " MACSTR, + *pos, MAC2STR(sa)); + return 0; + } + + adv_proto = pos; + pos += 2 + pos[1]; + + /* Query Response Length */ + if (pos + 2 > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length"); + return 0; + } + resp_len = WPA_GET_LE16(pos); + pos += 2; + + if (pos + resp_len > data + len) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " + "response from " MACSTR, MAC2STR(sa)); + return 0; + } + + if (pos + resp_len < data + len) { + wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " + "after Query Response from " MACSTR, + (unsigned int) (data + len - pos - resp_len), + MAC2STR(sa)); + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) + gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len, + frag_id, more_frags, comeback_delay); + else + gas_query_rx_initial(gas, query, adv_proto, pos, resp_len, + comeback_delay); + + return 0; +} + + +static void gas_query_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR, + MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_TIMEOUT); +} + + +static int gas_query_dialog_token_available(struct gas_query *gas, + const u8 *dst, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 && + dialog_token == q->dialog_token) + return 0; + } + + return 1; +} + + +int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx) +{ + struct gas_query_pending *query; + int dialog_token; + + if (wpabuf_len(req) < 3) + return -1; + + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available(gas, dst, dialog_token)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + + query = os_zalloc(sizeof(*query)); + if (query == NULL) + return -1; + + os_memcpy(query->addr, dst, ETH_ALEN); + query->dialog_token = dialog_token; + query->freq = freq; + query->cb = cb; + query->ctx = ctx; + dl_list_add(&gas->pending, &query->list); + + *(wpabuf_mhead_u8(req) + 2) = dialog_token; + + wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR + " dialog_token %u", MAC2STR(dst), dialog_token); + if (gas_query_tx(gas, query, req) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + os_free(query); + return -1; + } + + eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout, + gas, query); + + return dialog_token; +} + + +void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token) +{ + struct gas_query_pending *query; + + query = gas_query_get_pending(gas, dst, dialog_token); + if (query) + gas_query_done(gas, query, GAS_QUERY_CANCELLED); + +} diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h new file mode 100644 index 0000000..64c3825 --- /dev/null +++ b/wpa_supplicant/gas_query.h @@ -0,0 +1,61 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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. + */ + +#ifndef GAS_QUERY_H +#define GAS_QUERY_H + +struct gas_query; + +#ifdef CONFIG_GAS + +struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s); +void gas_query_deinit(struct gas_query *gas); +int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, + const u8 *bssid, const u8 *data, size_t len, int freq); + +enum gas_query_result { + GAS_QUERY_SUCCESS, + GAS_QUERY_FAILURE, + GAS_QUERY_TIMEOUT, + GAS_QUERY_PEER_ERROR, + GAS_QUERY_INTERNAL_ERROR, + GAS_QUERY_CANCELLED, + GAS_QUERY_DELETED_AT_DEINIT +}; + +int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx); +void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token); + +#else /* CONFIG_GAS */ + +static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s) +{ + return (void *) 1; +} + +static inline void gas_query_deinit(struct gas_query *gas) +{ +} + +#endif /* CONFIG_GAS */ + + +#endif /* GAS_QUERY_H */ diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 0e33253..fc1a586 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -39,6 +39,13 @@ static void supp_set_state(void *ctx, enum wpa_states state) } +static enum wpa_states supp_get_state(void *ctx) +{ + struct ibss_rsn_peer *peer = ctx; + return peer->supp_state; +} + + static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, size_t len) { @@ -125,6 +132,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg, } } + if (is_broadcast_ether_addr(addr)) + addr = peer->addr; return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } @@ -153,6 +162,12 @@ static void supp_cancel_auth_timeout(void *ctx) } +static void supp_deauthenticate(void * ctx, int reason_code) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__); +} + + int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, const u8 *psk) { @@ -163,6 +178,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, ctx->ctx = peer; ctx->msg_ctx = peer->ibss_rsn->wpa_s; ctx->set_state = supp_set_state; + ctx->get_state = supp_get_state; ctx->ether_send = supp_ether_send; ctx->get_beacon_ie = supp_get_beacon_ie; ctx->alloc_eapol = supp_alloc_eapol; @@ -170,6 +186,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, ctx->get_network_ctx = supp_get_network_ctx; ctx->mlme_setprotection = supp_mlme_setprotection; ctx->cancel_auth_timeout = supp_cancel_auth_timeout; + ctx->deauthenticate = supp_deauthenticate; peer->supp = wpa_sm_init(ctx); if (peer->supp == NULL) { wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); @@ -273,6 +290,24 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, } +static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), + void *cb_ctx) +{ + struct ibss_rsn *ibss_rsn = ctx; + struct ibss_rsn_peer *peer; + + wpa_printf(MSG_DEBUG, "AUTH: for_each_sta"); + + for (peer = ibss_rsn->peers; peer; peer = peer->next) { + if (peer->auth && cb(peer->auth, cb_ctx)) + return 1; + } + + return 0; +} + + static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, const u8 *own_addr) { @@ -288,6 +323,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, conf.rsn_pairwise = WPA_CIPHER_CCMP; conf.wpa_group = WPA_CIPHER_CCMP; conf.eapol_version = 2; + conf.wpa_group_rekey = 600; os_memset(&cb, 0, sizeof(cb)); cb.ctx = ibss_rsn; @@ -295,6 +331,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, cb.send_eapol = auth_send_eapol; cb.get_psk = auth_get_psk; cb.set_key = auth_set_key; + cb.for_each_sta = auth_for_each_sta; ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); if (ibss_rsn->auth_group == NULL) { @@ -302,6 +339,8 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, return -1; } + wpa_init_keys(ibss_rsn->auth_group); + return 0; } @@ -341,6 +380,18 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) { struct ibss_rsn_peer *peer; + if (ibss_rsn == NULL) + return -1; + + for (peer = ibss_rsn->peers; peer; peer = peer->next) { + if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and " + "Supplicant for peer " MACSTR " already " + "running", MAC2STR(addr)); + return 0; + } + } + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and " "Supplicant for peer " MACSTR, MAC2STR(addr)); @@ -369,6 +420,46 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) } +void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) +{ + struct ibss_rsn_peer *peer, *prev; + + if (ibss_rsn == NULL) + return; + + if (peermac == NULL) { + /* remove all peers */ + wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__); + peer = ibss_rsn->peers; + while (peer) { + prev = peer; + peer = peer->next; + ibss_rsn_free(prev); + ibss_rsn->peers = peer; + } + } else { + /* remove specific peer */ + wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR, + __func__, MAC2STR(peermac)); + + for (prev = NULL, peer = ibss_rsn->peers; peer != NULL; + prev = peer, peer = peer->next) { + if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) { + if (prev == NULL) + ibss_rsn->peers = peer->next; + else + prev->next = peer->next; + ibss_rsn_free(peer); + wpa_printf(MSG_DEBUG, "%s: Successfully " + "removed a specific peer", + __func__); + break; + } + } + } +} + + struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) { struct ibss_rsn *ibss_rsn; @@ -483,6 +574,9 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, { struct ibss_rsn_peer *peer; + if (ibss_rsn == NULL) + return -1; + for (peer = ibss_rsn->peers; peer; peer = peer->next) { if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0) return ibss_rsn_process_rx_eapol(ibss_rsn, peer, @@ -506,5 +600,7 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk) { + if (ibss_rsn == NULL) + return; os_memcpy(ibss_rsn->psk, psk, PMK_LEN); } diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h index 11e63ad..dbc889f 100644 --- a/wpa_supplicant/ibss_rsn.h +++ b/wpa_supplicant/ibss_rsn.h @@ -42,6 +42,7 @@ struct ibss_rsn { struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s); void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn); int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr); +void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac); int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, const u8 *buf, size_t len); void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk); diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c new file mode 100644 index 0000000..cd074a3 --- /dev/null +++ b/wpa_supplicant/interworking.c @@ -0,0 +1,1138 @@ +/* + * Interworking (IEEE 802.11u) + * Copyright (c) 2011, Qualcomm Atheros + * + * 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 "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "drivers/driver.h" +#include "eap_common/eap_defs.h" +#include "eap_peer/eap_methods.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "bss.h" +#include "scan.h" +#include "notify.h" +#include "gas_query.h" +#include "interworking.h" + + +#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC) +#define INTERWORKING_3GPP +#else +#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC) +#define INTERWORKING_3GPP +#else +#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC) +#define INTERWORKING_3GPP +#endif +#endif +#endif + +static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); + + +static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids, + struct wpabuf *extra) +{ + struct wpabuf *buf; + size_t i; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 + + (extra ? wpabuf_len(extra) : 0)); + if (buf == NULL) + return NULL; + + len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST); + for (i = 0; i < num_ids; i++) + wpabuf_put_le16(buf, info_ids[i]); + gas_anqp_set_element_len(buf, len_pos); + if (extra) + wpabuf_put_buf(buf, extra); + + gas_anqp_set_len(buf); + + return buf; +} + + +static void interworking_anqp_resp_cb(void *ctx, const u8 *dst, + u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, + u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp, + status_code); + interworking_next_anqp_fetch(wpa_s); +} + + +static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + struct wpabuf *buf; + int ret = 0; + int res; + u16 info_ids[] = { + ANQP_CAPABILITY_LIST, + ANQP_VENUE_NAME, + ANQP_NETWORK_AUTH_TYPE, + ANQP_ROAMING_CONSORTIUM, + ANQP_IP_ADDR_TYPE_AVAILABILITY, + ANQP_NAI_REALM, + ANQP_3GPP_CELLULAR_NETWORK, + ANQP_DOMAIN_NAME + }; + struct wpabuf *extra = NULL; + + wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, + MAC2STR(bss->bssid)); + + buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]), + extra); + wpabuf_free(extra); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf, + interworking_anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} + + +struct nai_realm_eap { + u8 method; + u8 inner_method; + enum nai_realm_eap_auth_inner_non_eap inner_non_eap; + u8 cred_type; + u8 tunneled_cred_type; +}; + +struct nai_realm { + u8 encoding; + char *realm; + u8 eap_count; + struct nai_realm_eap *eap; +}; + + +static void nai_realm_free(struct nai_realm *realms, u16 count) +{ + u16 i; + + if (realms == NULL) + return; + for (i = 0; i < count; i++) { + os_free(realms[i].eap); + os_free(realms[i].realm); + } + os_free(realms); +} + + +static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, + const u8 *end) +{ + u8 elen, auth_count, a; + const u8 *e_end; + + if (pos + 3 > end) { + wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields"); + return NULL; + } + + elen = *pos++; + if (pos + elen > end || elen < 2) { + wpa_printf(MSG_DEBUG, "No room for EAP Method subfield"); + return NULL; + } + e_end = pos + elen; + e->method = *pos++; + auth_count = *pos++; + wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u", + elen, e->method, auth_count); + + for (a = 0; a < auth_count; a++) { + u8 id, len; + + if (pos + 2 > end || pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "No room for Authentication " + "Parameter subfield"); + return NULL; + } + + id = *pos++; + len = *pos++; + + switch (id) { + case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH: + if (len < 1) + break; + e->inner_non_eap = *pos; + if (e->method != EAP_TYPE_TTLS) + break; + switch (*pos) { + case NAI_REALM_INNER_NON_EAP_PAP: + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP"); + break; + case NAI_REALM_INNER_NON_EAP_CHAP: + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP"); + break; + case NAI_REALM_INNER_NON_EAP_MSCHAP: + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP"); + break; + case NAI_REALM_INNER_NON_EAP_MSCHAPV2: + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2"); + break; + } + break; + case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD: + if (len < 1) + break; + e->inner_method = *pos; + wpa_printf(MSG_DEBUG, "Inner EAP method: %u", + e->inner_method); + break; + case NAI_REALM_EAP_AUTH_CRED_TYPE: + if (len < 1) + break; + e->cred_type = *pos; + wpa_printf(MSG_DEBUG, "Credential Type: %u", + e->cred_type); + break; + case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE: + if (len < 1) + break; + e->tunneled_cred_type = *pos; + wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential " + "Type: %u", e->tunneled_cred_type); + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported Authentication " + "Parameter: id=%u len=%u", id, len); + wpa_hexdump(MSG_DEBUG, "Authentication Parameter " + "Value", pos, len); + break; + } + + pos += len; + } + + return e_end; +} + + +static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, + const u8 *end) +{ + u16 len; + const u8 *f_end; + u8 realm_len, e; + + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " + "fixed fields"); + return NULL; + } + + len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */ + pos += 2; + if (pos + len > end || len < 3) { + wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " + "(len=%u; left=%u)", + len, (unsigned int) (end - pos)); + return NULL; + } + f_end = pos + len; + + r->encoding = *pos++; + realm_len = *pos++; + if (pos + realm_len > f_end) { + wpa_printf(MSG_DEBUG, "No room for NAI Realm " + "(len=%u; left=%u)", + realm_len, (unsigned int) (f_end - pos)); + return NULL; + } + wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len); + r->realm = os_malloc(realm_len + 1); + if (r->realm == NULL) + return NULL; + os_memcpy(r->realm, pos, realm_len); + r->realm[realm_len] = '\0'; + pos += realm_len; + + if (pos + 1 > f_end) { + wpa_printf(MSG_DEBUG, "No room for EAP Method Count"); + return NULL; + } + r->eap_count = *pos++; + wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count); + if (pos + r->eap_count * 3 > f_end) { + wpa_printf(MSG_DEBUG, "No room for EAP Methods"); + return NULL; + } + r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap)); + if (r->eap == NULL) + return NULL; + + for (e = 0; e < r->eap_count; e++) { + pos = nai_realm_parse_eap(&r->eap[e], pos, f_end); + if (pos == NULL) + return NULL; + } + + return f_end; +} + + +static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) +{ + struct nai_realm *realm; + const u8 *pos, *end; + u16 i, num; + + if (anqp == NULL || wpabuf_len(anqp) < 2) + return NULL; + + pos = wpabuf_head_u8(anqp); + end = pos + wpabuf_len(anqp); + num = WPA_GET_LE16(pos); + wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num); + pos += 2; + + if (num * 5 > end - pos) { + wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not " + "enough data (%u octets) for that many realms", + num, (unsigned int) (end - pos)); + return NULL; + } + + realm = os_zalloc(num * sizeof(struct nai_realm)); + if (realm == NULL) + return NULL; + + for (i = 0; i < num; i++) { + pos = nai_realm_parse_realm(&realm[i], pos, end); + if (pos == NULL) { + nai_realm_free(realm, num); + return NULL; + } + } + + *count = num; + return realm; +} + + +static int nai_realm_match(struct nai_realm *realm, const char *home_realm) +{ + char *tmp, *pos, *end; + int match = 0; + + if (realm->realm == NULL || home_realm == NULL) + return 0; + + if (os_strchr(realm->realm, ';') == NULL) + return os_strcasecmp(realm->realm, home_realm) == 0; + + tmp = os_strdup(realm->realm); + if (tmp == NULL) + return 0; + + pos = tmp; + while (*pos) { + end = os_strchr(pos, ';'); + if (end) + *end = '\0'; + if (os_strcasecmp(pos, home_realm) == 0) { + match = 1; + break; + } + if (end == NULL) + break; + pos = end + 1; + } + + os_free(tmp); + + return match; +} + + +static int nai_realm_cred_username(struct nai_realm_eap *eap) +{ + if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) + return 0; /* method not supported */ + + if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) { + /* Only tunneled methods with username/password supported */ + return 0; + } + + if (eap->method == EAP_TYPE_PEAP && + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + return 0; + + if (eap->method == EAP_TYPE_TTLS) { + if (eap->inner_method == 0 && eap->inner_non_eap == 0) + return 0; + if (eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + return 0; + if (eap->inner_non_eap && + eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP && + eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP && + eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP && + eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) + return 0; + } + + if (eap->inner_method && + eap->inner_method != EAP_TYPE_GTC && + eap->inner_method != EAP_TYPE_MSCHAPV2) + return 0; + + return 1; +} + + +struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s, + struct nai_realm *realm) +{ + u8 e; + + if (wpa_s->conf->home_username == NULL || + wpa_s->conf->home_username[0] == '\0' || + wpa_s->conf->home_password == NULL || + wpa_s->conf->home_password[0] == '\0') + return NULL; + + for (e = 0; e < realm->eap_count; e++) { + struct nai_realm_eap *eap = &realm->eap[e]; + if (nai_realm_cred_username(eap)) + return eap; + } + + return NULL; +} + + +#ifdef INTERWORKING_3GPP + +static int plmn_id_match(struct wpabuf *anqp, const char *imsi) +{ + const char *sep; + u8 plmn[3]; + const u8 *pos, *end; + u8 udhl; + + sep = os_strchr(imsi, '-'); + if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6)) + return 0; + + /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ + plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); + plmn[1] = imsi[2] - '0'; + if (sep - imsi == 6) + plmn[1] |= (imsi[5] - '0') << 4; + else + plmn[1] |= 0xf0; + plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); + + if (anqp == NULL) + return 0; + pos = wpabuf_head_u8(anqp); + end = pos + wpabuf_len(anqp); + if (pos + 2 > end) + return 0; + if (*pos != 0) { + wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos); + return 0; + } + pos++; + udhl = *pos++; + if (pos + udhl > end) { + wpa_printf(MSG_DEBUG, "Invalid UDHL"); + return 0; + } + end = pos + udhl; + + while (pos + 2 <= end) { + u8 iei, len; + const u8 *l_end; + iei = *pos++; + len = *pos++ & 0x7f; + if (pos + len > end) + break; + l_end = pos + len; + + if (iei == 0 && len > 0) { + /* PLMN List */ + u8 num, i; + num = *pos++; + for (i = 0; i < num; i++) { + if (pos + 3 > end) + break; + if (os_memcmp(pos, plmn, 3) == 0) + return 1; /* Found matching PLMN */ + } + } + + pos = l_end; + } + + return 0; +} + + +static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) +{ + const char *sep, *msin; + char nai[100], *end, *pos; + size_t msin_len, plmn_len; + + /* + * TS 23.003, Clause 14 (3GPP to WLAN Interworking) + * Root NAI: + * @wlan.mnc.mcc.3gppnetwork.org + * is zero-padded to three digits in case two-digit MNC is used + */ + + if (imsi == NULL || os_strlen(imsi) > 16) { + wpa_printf(MSG_DEBUG, "No valid IMSI available"); + return -1; + } + sep = os_strchr(imsi, '-'); + if (sep == NULL) + return -1; + plmn_len = sep - imsi; + if (plmn_len != 5 && plmn_len != 6) + return -1; + msin = sep + 1; + msin_len = os_strlen(msin); + + pos = nai; + end = pos + sizeof(nai); + *pos++ = prefix; + os_memcpy(pos, imsi, plmn_len); + pos += plmn_len; + os_memcpy(pos, msin, msin_len); + pos += msin_len; + pos += os_snprintf(pos, end - pos, "@wlan.mnc"); + if (plmn_len == 5) { + *pos++ = '0'; + *pos++ = imsi[3]; + *pos++ = imsi[4]; + } else { + *pos++ = imsi[3]; + *pos++ = imsi[4]; + *pos++ = imsi[5]; + } + pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", + imsi[0], imsi[1], imsi[2]); + + return wpa_config_set_quoted(ssid, "identity", nai); +} + +#endif /* INTERWORKING_3GPP */ + + +static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ +#ifdef INTERWORKING_3GPP + struct wpa_ssid *ssid; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + return -1; + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", + MAC2STR(bss->bssid)); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->ssid = os_zalloc(ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + + /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */ + if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM not supported"); + goto fail; + } + if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) { + wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); + goto fail; + } + + if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) { + if (wpa_config_set_quoted(ssid, "password", + wpa_s->conf->home_milenage) < 0) + goto fail; + } else { + /* TODO: PIN */ + if (wpa_config_set_quoted(ssid, "pcsc", "") < 0) + goto fail; + } + + if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] && + wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password) + < 0) + goto fail; + + wpa_supplicant_select_network(wpa_s, ssid); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); +#endif /* INTERWORKING_3GPP */ + return -1; +} + + +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + struct nai_realm *realm; + struct nai_realm_eap *eap = NULL; + u16 count, i; + char buf[100]; + const u8 *ie; + + if (bss == NULL) + return -1; + ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL || ie[1] == 0) { + wpa_printf(MSG_DEBUG, "Interworking: No SSID known for " + MACSTR, MAC2STR(bss->bssid)); + return -1; + } + + realm = nai_realm_parse(bss->anqp_nai_realm, &count); + if (realm == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " + "Realm list from " MACSTR, MAC2STR(bss->bssid)); + count = 0; + } + + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm)) + continue; + eap = nai_realm_find_eap(wpa_s, &realm[i]); + if (eap) + break; + } + + if (!eap) { + if (interworking_connect_3gpp(wpa_s, bss) == 0) { + if (realm) + nai_realm_free(realm, count); + return 0; + } + + wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " + "and EAP method found for " MACSTR, + MAC2STR(bss->bssid)); + nai_realm_free(realm, count); + return -1; + } + + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, + MAC2STR(bss->bssid)); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + nai_realm_free(realm, count); + return -1; + } + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->ssid = os_zalloc(ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + + if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, + eap->method), 0) < 0) + goto fail; + + if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] && + wpa_config_set_quoted(ssid, "identity", + wpa_s->conf->home_username) < 0) + goto fail; + + if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] && + wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password) + < 0) + goto fail; + + switch (eap->method) { + case EAP_TYPE_TTLS: + if (eap->inner_method) { + os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", + eap_get_name(EAP_VENDOR_IETF, + eap->inner_method)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + } + switch (eap->inner_non_eap) { + case NAI_REALM_INNER_NON_EAP_PAP: + if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) < + 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_CHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0) + < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"", + 0) < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAPV2: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", + 0) < 0) + goto fail; + break; + } + break; + case EAP_TYPE_PEAP: + os_snprintf(buf, sizeof(buf), "\"auth=%s\"", + eap_get_name(EAP_VENDOR_IETF, eap->inner_method)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + } + + if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] && + wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) < + 0) + goto fail; + + nai_realm_free(realm, count); + + wpa_supplicant_select_network(wpa_s, ssid); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + nai_realm_free(realm, count); + return -1; +} + + +static int interworking_credentials_available_3gpp( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + int ret = 0; + +#ifdef INTERWORKING_3GPP + if (bss->anqp_3gpp == NULL) + return ret; + + if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] || + wpa_s->conf->home_milenage == NULL || + !wpa_s->conf->home_milenage[0]) + return ret; + + wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR, + MAC2STR(bss->bssid)); + ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi); + wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); +#endif /* INTERWORKING_3GPP */ + return ret; +} + + +static int interworking_credentials_available_realm( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct nai_realm *realm; + u16 count, i; + int found = 0; + + if (bss->anqp_nai_realm == NULL) + return 0; + + if (wpa_s->conf->home_realm == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); + realm = nai_realm_parse(bss->anqp_nai_realm, &count); + if (realm == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " + "Realm list from " MACSTR, MAC2STR(bss->bssid)); + return 0; + } + + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm)) + continue; + if (nai_realm_find_eap(wpa_s, &realm[i])) { + found++; + break; + } + } + + nai_realm_free(realm, count); + + return found; +} + + +static int interworking_credentials_available(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + return interworking_credentials_available_realm(wpa_s, bss) || + interworking_credentials_available_3gpp(wpa_s, bss); +} + + +static void interworking_select_network(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *selected = NULL; + unsigned int count = 0; + + wpa_s->network_select = 0; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!interworking_credentials_available(wpa_s, bss)) + continue; + count++; + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR, + MAC2STR(bss->bssid)); + if (selected == NULL && wpa_s->auto_select) + selected = bss; + } + + if (count == 0) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " + "with matching credentials found"); + } + + if (selected) + interworking_connect(wpa_s, selected); +} + + +static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + int found = 0; + const u8 *ie; + + if (!wpa_s->fetch_anqp_in_progress) + return; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!(bss->caps & IEEE80211_CAP_ESS)) + continue; + ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); + if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80)) + continue; /* AP does not support Interworking */ + + if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) { + found++; + bss->flags |= WPA_BSS_ANQP_FETCH_TRIED; + wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for " + MACSTR, MAC2STR(bss->bssid)); + interworking_anqp_send_req(wpa_s, bss); + break; + } + } + + if (found == 0) { + wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); + wpa_s->fetch_anqp_in_progress = 0; + if (wpa_s->network_select) + interworking_select_network(wpa_s); + } +} + + +static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) + bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED; + + wpa_s->fetch_anqp_in_progress = 1; + interworking_next_anqp_fetch(wpa_s); +} + + +int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) + return 0; + + wpa_s->network_select = 0; + + interworking_start_fetch_anqp(wpa_s); + + return 0; +} + + +void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->fetch_anqp_in_progress) + return; + + wpa_s->fetch_anqp_in_progress = 0; +} + + +int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, + u16 info_ids[], size_t num_ids) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) + freq = bss->freq; + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", + MAC2STR(dst), (unsigned int) num_ids); + + buf = anqp_build_req(info_ids, num_ids, NULL); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} + + +static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 info_id, + const u8 *data, size_t slen) +{ + const u8 *pos = data; + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); + + switch (info_id) { + case ANQP_CAPABILITY_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " ANQP Capability list", MAC2STR(sa)); + break; + case ANQP_VENUE_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Venue Name", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen); + if (bss) { + wpabuf_free(bss->anqp_venue_name); + bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_NETWORK_AUTH_TYPE: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Network Authentication Type information", + MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " + "Type", pos, slen); + if (bss) { + wpabuf_free(bss->anqp_network_auth_type); + bss->anqp_network_auth_type = + wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_ROAMING_CONSORTIUM: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Roaming Consortium list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", + pos, slen); + if (bss) { + wpabuf_free(bss->anqp_roaming_consortium); + bss->anqp_roaming_consortium = + wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " IP Address Type Availability information", + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", + pos, slen); + if (bss) { + wpabuf_free(bss->anqp_ip_addr_type_availability); + bss->anqp_ip_addr_type_availability = + wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_NAI_REALM: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " NAI Realm list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen); + if (bss) { + wpabuf_free(bss->anqp_nai_realm); + bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_3GPP_CELLULAR_NETWORK: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " 3GPP Cellular Network information", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network", + pos, slen); + if (bss) { + wpabuf_free(bss->anqp_3gpp); + bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_DOMAIN_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Domain Name list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen); + if (bss) { + wpabuf_free(bss->anqp_domain_name); + bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_VENDOR_SPECIFIC: + if (slen < 3) + return; + + switch (WPA_GET_BE24(pos)) { + default: + wpa_printf(MSG_DEBUG, "Interworking: Unsupported " + "vendor-specific ANQP OUI %06x", + WPA_GET_BE24(pos)); + return; + } + break; + default: + wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID " + "%u", info_id); + break; + } +} + + +void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos; + const u8 *end; + u16 info_id; + u16 slen; + + if (result != GAS_QUERY_SUCCESS) + return; + + pos = wpabuf_head(adv_proto); + if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO || + pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { + wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement " + "Protocol in response"); + return; + } + + pos = wpabuf_head(resp); + end = pos + wpabuf_len(resp); + + while (pos < end) { + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid element"); + break; + } + info_id = WPA_GET_LE16(pos); + pos += 2; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid element length " + "for Info ID %u", info_id); + break; + } + interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos, + slen); + pos += slen; + } +} + + +static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start " + "ANQP fetch"); + interworking_start_fetch_anqp(wpa_s); +} + + +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) +{ + interworking_stop_fetch_anqp(wpa_s); + wpa_s->network_select = 1; + wpa_s->auto_select = !!auto_select; + wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " + "selection"); + wpa_s->scan_res_handler = interworking_scan_res_handler; + wpa_s->scan_req = 2; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h new file mode 100644 index 0000000..247df30 --- /dev/null +++ b/wpa_supplicant/interworking.h @@ -0,0 +1,31 @@ +/* + * Interworking (IEEE 802.11u) + * Copyright (c) 2011, Qualcomm Atheros + * + * 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. + */ + +#ifndef INTERWORKING_H +#define INTERWORKING_H + +enum gas_query_result; + +int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, + u16 info_ids[], size_t num_ids); +void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code); +int interworking_fetch_anqp(struct wpa_supplicant *wpa_s); +void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s); +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select); +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); + +#endif /* INTERWORKING_H */ diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index c0aa59c..e196f3c 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -33,7 +33,8 @@ static void usage(void) "[-g] \\\n" " -i -c [-C] [-D] " "[-p] \\\n" - " [-b] [-f] \\\n" + " [-b] [-f] [-e] " + "\\\n" " [-o] [-O] \\\n" " [-N -i -c [-C] " "[-D] \\\n" @@ -56,7 +57,8 @@ static void usage(void) " -C = ctrl_interface parameter (only used if -c is not)\n" " -i = interface name\n" " -d = increase debugging verbosity (-dd even more)\n" - " -D = driver name (can be multiple drivers: nl80211,wext)\n"); + " -D = driver name (can be multiple drivers: nl80211,wext)\n" + " -e = entropy file\n"); #ifdef CONFIG_DEBUG_FILE printf(" -f = log output to debug file instead of stdout\n"); #endif /* CONFIG_DEBUG_FILE */ @@ -143,7 +145,7 @@ int main(int argc, char *argv[]) wpa_supplicant_fd_workaround(); for (;;) { - c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW"); + c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW"); if (c < 0) break; switch (c) { @@ -172,6 +174,9 @@ int main(int argc, char *argv[]) params.wpa_debug_level--; break; #endif /* CONFIG_NO_STDOUT_DEBUG */ + case 'e': + params.entropy_file = optarg; + break; #ifdef CONFIG_DEBUG_FILE case 'f': params.wpa_debug_file_path = optarg; diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c deleted file mode 100644 index eb60ac5..0000000 --- a/wpa_supplicant/mlme.c +++ /dev/null @@ -1,3198 +0,0 @@ -/* - * WPA Supplicant - Client mode MLME - * Copyright (c) 2003-2008, Jouni Malinen - * Copyright (c) 2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * - * 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 "eloop.h" -#include "config_ssid.h" -#include "wpa_supplicant_i.h" -#include "notify.h" -#include "driver_i.h" -#include "rsn_supp/wpa.h" -#include "common/ieee802_11_defs.h" -#include "common/ieee802_11_common.h" -#include "mlme.h" - - -/* Timeouts and intervals in milliseconds */ -#define IEEE80211_AUTH_TIMEOUT (200) -#define IEEE80211_AUTH_MAX_TRIES 3 -#define IEEE80211_ASSOC_TIMEOUT (200) -#define IEEE80211_ASSOC_MAX_TRIES 3 -#define IEEE80211_MONITORING_INTERVAL (2000) -#define IEEE80211_PROBE_INTERVAL (60000) -#define IEEE80211_RETRY_AUTH_INTERVAL (1000) -#define IEEE80211_SCAN_INTERVAL (2000) -#define IEEE80211_SCAN_INTERVAL_SLOW (15000) -#define IEEE80211_IBSS_JOIN_TIMEOUT (20000) - -#define IEEE80211_PROBE_DELAY (33) -#define IEEE80211_CHANNEL_TIME (33) -#define IEEE80211_PASSIVE_CHANNEL_TIME (200) -#define IEEE80211_SCAN_RESULT_EXPIRE (10000) -#define IEEE80211_IBSS_MERGE_INTERVAL (30000) -#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000) - -#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 - - -#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) - - -struct ieee80211_sta_bss { - struct ieee80211_sta_bss *next; - struct ieee80211_sta_bss *hnext; - - u8 bssid[ETH_ALEN]; - u8 ssid[MAX_SSID_LEN]; - size_t ssid_len; - u16 capability; /* host byte order */ - int hw_mode; - int channel; - int freq; - int rssi; - u8 *ie; - size_t ie_len; - u8 *wpa_ie; - size_t wpa_ie_len; - u8 *rsn_ie; - size_t rsn_ie_len; - u8 *wmm_ie; - size_t wmm_ie_len; - u8 *mdie; - size_t mdie_len; -#define IEEE80211_MAX_SUPP_RATES 32 - u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; - size_t supp_rates_len; - int beacon_int; - u64 timestamp; - - int probe_resp; - struct os_time last_update; -}; - - -static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, - const u8 *dst, - const u8 *ssid, size_t ssid_len); -static struct ieee80211_sta_bss * -ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid); -static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s); -static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s); -static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx); -static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx); -static void ieee80211_build_tspec(struct wpabuf *buf); -static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, - const u8 *ies, size_t ies_len); - - -static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s, - enum hostapd_hw_mode phymode, int chan, - int freq) -{ - size_t i; - struct hostapd_hw_modes *mode; - - for (i = 0; i < wpa_s->mlme.num_modes; i++) { - mode = &wpa_s->mlme.modes[i]; - if (mode->mode == phymode) { - wpa_s->mlme.curr_rates = mode->rates; - wpa_s->mlme.num_curr_rates = mode->num_rates; - break; - } - } - - return wpa_drv_set_channel(wpa_s, phymode, chan, freq); -} - - -static int ecw2cw(int ecw) -{ - int cw = 1; - while (ecw > 0) { - cw <<= 1; - ecw--; - } - return cw - 1; -} - - -static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s, - const u8 *wmm_param, size_t wmm_param_len) -{ - size_t left; - int count; - const u8 *pos; - u8 wmm_acm; - - if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) - return; - count = wmm_param[6] & 0x0f; - if (count == wpa_s->mlme.wmm_last_param_set) - return; - wpa_s->mlme.wmm_last_param_set = count; - - pos = wmm_param + 8; - left = wmm_param_len - 8; - - wmm_acm = 0; - for (; left >= 4; left -= 4, pos += 4) { - int aci = (pos[0] >> 5) & 0x03; - int acm = (pos[0] >> 4) & 0x01; - int aifs, cw_max, cw_min, burst_time; - - switch (aci) { - case 1: /* AC_BK */ - if (acm) - wmm_acm |= BIT(1) | BIT(2); /* BK/- */ - break; - case 2: /* AC_VI */ - if (acm) - wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ - break; - case 3: /* AC_VO */ - if (acm) - wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ - break; - case 0: /* AC_BE */ - default: - if (acm) - wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ - break; - } - - aifs = pos[0] & 0x0f; - cw_max = ecw2cw((pos[1] & 0xf0) >> 4); - cw_min = ecw2cw(pos[1] & 0x0f); - /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ - burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; - wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d " - "cWmin=%d cWmax=%d burst=%d", - aci, acm, aifs, cw_min, cw_max, burst_time); - /* TODO: driver configuration */ - } -} - - -static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc) -{ - if (wpa_s->mlme.associated == assoc && !assoc) - return; - - wpa_s->mlme.associated = assoc; - - if (assoc) { - union wpa_event_data data; - os_memset(&data, 0, sizeof(data)); - wpa_s->mlme.prev_bssid_set = 1; - os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); - data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies; - data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len; - data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies; - data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len; - data.assoc_info.freq = wpa_s->mlme.freq; - wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); - } else { - wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); - } - os_get_time(&wpa_s->mlme.last_probe); -} - - -static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf, - size_t len) -{ - return wpa_drv_send_mlme(wpa_s, buf, len); -} - - -static void ieee80211_send_auth(struct wpa_supplicant *wpa_s, - int transaction, const u8 *extra, - size_t extra_len, int encrypt) -{ - u8 *buf; - size_t len; - struct ieee80211_mgmt *mgmt; - - buf = os_malloc(sizeof(*mgmt) + 6 + extra_len); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " - "auth frame"); - return; - } - - mgmt = (struct ieee80211_mgmt *) buf; - len = 24 + 6; - os_memset(mgmt, 0, 24 + 6); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - if (encrypt) - mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg); - mgmt->u.auth.auth_transaction = host_to_le16(transaction); - wpa_s->mlme.auth_transaction = transaction + 1; - mgmt->u.auth.status_code = host_to_le16(0); - if (extra) { - os_memcpy(buf + len, extra, extra_len); - len += extra_len; - } - - ieee80211_sta_tx(wpa_s, buf, len); - os_free(buf); -} - - -static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms) -{ - eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); - eloop_register_timeout(ms / 1000, 1000 * (ms % 1000), - ieee80211_sta_timer, wpa_s, NULL); -} - - -static void ieee80211_authenticate(struct wpa_supplicant *wpa_s) -{ - u8 *extra; - size_t extra_len; - - wpa_s->mlme.auth_tries++; - if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) { - wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR - " timed out", MAC2STR(wpa_s->bssid)); - return; - } - - wpa_s->mlme.state = IEEE80211_AUTHENTICATE; - wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR, - MAC2STR(wpa_s->bssid)); - - extra = NULL; - extra_len = 0; - -#ifdef CONFIG_IEEE80211R - if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || - wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && - wpa_s->mlme.ft_ies) { - struct ieee80211_sta_bss *bss; - struct rsn_mdie *mdie = NULL; - bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); - if (bss && bss->mdie_len >= 2 + sizeof(*mdie)) - mdie = (struct rsn_mdie *) (bss->mdie + 2); - if (mdie && - os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md, - MOBILITY_DOMAIN_ID_LEN) == 0) { - wpa_printf(MSG_DEBUG, "MLME: Trying to use FT " - "over-the-air"); - wpa_s->mlme.auth_alg = WLAN_AUTH_FT; - extra = wpa_s->mlme.ft_ies; - extra_len = wpa_s->mlme.ft_ies_len; - } - } -#endif /* CONFIG_IEEE80211R */ - - ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0); - - ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT); -} - - -static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s) -{ - struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *buf; - int i, len; - u16 capab; - struct ieee80211_sta_bss *bss; - int wmm = 0; - size_t blen, buflen; - - if (wpa_s->mlme.curr_rates == NULL) { - wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc"); - return; - } - - buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len + - wpa_s->mlme.ssid_len; -#ifdef CONFIG_IEEE80211R - if (wpa_s->mlme.ft_ies) - buflen += wpa_s->mlme.ft_ies_len; -#endif /* CONFIG_IEEE80211R */ - buf = os_malloc(buflen); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " - "assoc frame"); - return; - } - blen = 0; - - capab = wpa_s->mlme.capab; - if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) { - capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | - WLAN_CAPABILITY_SHORT_PREAMBLE; - } - bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); - if (bss) { - if (bss->capability & WLAN_CAPABILITY_PRIVACY) - capab |= WLAN_CAPABILITY_PRIVACY; - if (bss->wmm_ie) { - wmm = 1; - } - } - - mgmt = (struct ieee80211_mgmt *) buf; - blen += 24; - os_memset(mgmt, 0, 24); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - - if (wpa_s->mlme.prev_bssid_set) { - blen += 10; - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_REASSOC_REQ); - mgmt->u.reassoc_req.capab_info = host_to_le16(capab); - mgmt->u.reassoc_req.listen_interval = host_to_le16(1); - os_memcpy(mgmt->u.reassoc_req.current_ap, - wpa_s->mlme.prev_bssid, - ETH_ALEN); - } else { - blen += 4; - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ASSOC_REQ); - mgmt->u.assoc_req.capab_info = host_to_le16(capab); - mgmt->u.assoc_req.listen_interval = host_to_le16(1); - } - - /* SSID */ - ies = pos = buf + blen; - blen += 2 + wpa_s->mlme.ssid_len; - *pos++ = WLAN_EID_SSID; - *pos++ = wpa_s->mlme.ssid_len; - os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); - - len = wpa_s->mlme.num_curr_rates; - if (len > 8) - len = 8; - pos = buf + blen; - blen += len + 2; - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = len; - for (i = 0; i < len; i++) - *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5); - - if (wpa_s->mlme.num_curr_rates > len) { - pos = buf + blen; - blen += wpa_s->mlme.num_curr_rates - len + 2; - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = wpa_s->mlme.num_curr_rates - len; - for (i = len; i < wpa_s->mlme.num_curr_rates; i++) - *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5); - } - - if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) { - pos = buf + blen; - blen += wpa_s->mlme.extra_ie_len; - os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len); - } - -#ifdef CONFIG_IEEE80211R - if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || - wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && - wpa_s->mlme.auth_alg != WLAN_AUTH_FT && - bss && bss->mdie && - bss->mdie_len >= 2 + sizeof(struct rsn_mdie) && - bss->mdie[1] >= sizeof(struct rsn_mdie)) { - pos = buf + blen; - blen += 2 + sizeof(struct rsn_mdie); - *pos++ = WLAN_EID_MOBILITY_DOMAIN; - *pos++ = sizeof(struct rsn_mdie); - os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN); - pos += MOBILITY_DOMAIN_ID_LEN; - *pos++ = 0; /* FIX: copy from the target AP's MDIE */ - } - - if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X || - wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) && - wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) { - pos = buf + blen; - os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len); - pos += wpa_s->mlme.ft_ies_len; - blen += wpa_s->mlme.ft_ies_len; - } -#endif /* CONFIG_IEEE80211R */ - - if (wmm && wpa_s->mlme.wmm_enabled) { - pos = buf + blen; - blen += 9; - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = 7; /* len */ - *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *pos++ = 0x50; - *pos++ = 0xf2; - *pos++ = 2; /* WMM */ - *pos++ = 0; /* WMM info */ - *pos++ = 1; /* WMM ver */ - *pos++ = 0; - } - - os_free(wpa_s->mlme.assocreq_ies); - wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies; - wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len); - if (wpa_s->mlme.assocreq_ies) { - os_memcpy(wpa_s->mlme.assocreq_ies, ies, - wpa_s->mlme.assocreq_ies_len); - } - - ieee80211_sta_tx(wpa_s, buf, blen); - os_free(buf); -} - - -static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason) -{ - u8 *buf; - size_t len; - struct ieee80211_mgmt *mgmt; - - buf = os_zalloc(sizeof(*mgmt)); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " - "deauth frame"); - return; - } - - mgmt = (struct ieee80211_mgmt *) buf; - len = 24; - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DEAUTH); - len += 2; - mgmt->u.deauth.reason_code = host_to_le16(reason); - - ieee80211_sta_tx(wpa_s, buf, len); - os_free(buf); -} - - -static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason) -{ - u8 *buf; - size_t len; - struct ieee80211_mgmt *mgmt; - - buf = os_zalloc(sizeof(*mgmt)); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " - "disassoc frame"); - return; - } - - mgmt = (struct ieee80211_mgmt *) buf; - len = 24; - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DISASSOC); - len += 2; - mgmt->u.disassoc.reason_code = host_to_le16(reason); - - ieee80211_sta_tx(wpa_s, buf, len); - os_free(buf); -} - - -static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s) -{ - struct ieee80211_sta_bss *bss; - int res = 0; - - if (wpa_s->mlme.mixed_cell || - wpa_s->mlme.key_mgmt != KEY_MGMT_NONE) - return 0; - - bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); - if (bss == NULL) - return 0; - - if (ieee80211_sta_wep_configured(wpa_s) != - !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) - res = 1; - - return res; -} - - -static void ieee80211_associate(struct wpa_supplicant *wpa_s) -{ - wpa_s->mlme.assoc_tries++; - if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { - wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR - " timed out", MAC2STR(wpa_s->bssid)); - return; - } - - wpa_s->mlme.state = IEEE80211_ASSOCIATE; - wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR, - MAC2STR(wpa_s->bssid)); - if (ieee80211_privacy_mismatch(wpa_s)) { - wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy " - "configuration and mixed-cell disabled - abort " - "association"); - return; - } - - ieee80211_send_assoc(wpa_s); - - ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT); -} - - -static void ieee80211_associated(struct wpa_supplicant *wpa_s) -{ - int disassoc; - - /* TODO: start monitoring current AP signal quality and number of - * missed beacons. Scan other channels every now and then and search - * for better APs. */ - /* TODO: remove expired BSSes */ - - wpa_s->mlme.state = IEEE80211_ASSOCIATED; - -#if 0 /* FIX */ - sta = sta_info_get(local, wpa_s->bssid); - if (sta == NULL) { - wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR, - MAC2STR(wpa_s->bssid)); - disassoc = 1; - } else { - disassoc = 0; - if (time_after(jiffies, - sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { - if (wpa_s->mlme.probereq_poll) { - wpa_printf(MSG_DEBUG "MLME: No ProbeResp from " - "current AP " MACSTR " - assume " - "out of range", - MAC2STR(wpa_s->bssid)); - disassoc = 1; - } else { - ieee80211_send_probe_req( - wpa_s->bssid, - wpa_s->mlme.scan_ssid, - wpa_s->mlme.scan_ssid_len); - wpa_s->mlme.probereq_poll = 1; - } - } else { - wpa_s->mlme.probereq_poll = 0; - if (time_after(jiffies, wpa_s->mlme.last_probe + - IEEE80211_PROBE_INTERVAL)) { - wpa_s->mlme.last_probe = jiffies; - ieee80211_send_probe_req(wpa_s->bssid, - wpa_s->mlme.ssid, - wpa_s->mlme.ssid_len); - } - } - sta_info_release(local, sta); - } -#else - disassoc = 0; -#endif - if (disassoc) { - wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL); - ieee80211_reschedule_timer(wpa_s, - IEEE80211_MONITORING_INTERVAL + - 30000); - } else { - ieee80211_reschedule_timer(wpa_s, - IEEE80211_MONITORING_INTERVAL); - } -} - - -static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s, - const u8 *dst, - const u8 *ssid, size_t ssid_len) -{ - u8 *buf; - size_t len; - struct ieee80211_mgmt *mgmt; - u8 *pos, *supp_rates; - u8 *esupp_rates = NULL; - int i; - - buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for " - "probe request"); - return; - } - - mgmt = (struct ieee80211_mgmt *) buf; - len = 24; - os_memset(mgmt, 0, 24); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_PROBE_REQ); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - if (dst) { - os_memcpy(mgmt->da, dst, ETH_ALEN); - os_memcpy(mgmt->bssid, dst, ETH_ALEN); - } else { - os_memset(mgmt->da, 0xff, ETH_ALEN); - os_memset(mgmt->bssid, 0xff, ETH_ALEN); - } - pos = buf + len; - len += 2 + ssid_len; - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - os_memcpy(pos, ssid, ssid_len); - - supp_rates = buf + len; - len += 2; - supp_rates[0] = WLAN_EID_SUPP_RATES; - supp_rates[1] = 0; - for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) { - if (esupp_rates) { - pos = buf + len; - len++; - esupp_rates[1]++; - } else if (supp_rates[1] == 8) { - esupp_rates = pos; - esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; - esupp_rates[1] = 1; - pos = &esupp_rates[2]; - len += 3; - } else { - pos = buf + len; - len++; - supp_rates[1]++; - } - *pos++ = wpa_s->mlme.curr_rates[i] / 5; - } - - if (wpa_s->mlme.extra_probe_ie) { - os_memcpy(pos, wpa_s->mlme.extra_probe_ie, - wpa_s->mlme.extra_probe_ie_len); - len += wpa_s->mlme.extra_probe_ie_len; - } - - ieee80211_sta_tx(wpa_s, buf, len); - os_free(buf); -} - - -static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s) -{ -#if 0 /* FIX */ - if (sdata == NULL || sdata->default_key == NULL || - sdata->default_key->alg != ALG_WEP) - return 0; - return 1; -#else - return 0; -#endif -} - - -static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s) -{ - wpa_printf(MSG_DEBUG, "MLME: authenticated"); - wpa_s->mlme.authenticated = 1; - ieee80211_associate(wpa_s); -} - - -static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - u8 *pos; - struct ieee802_11_elems elems; - - wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge"); - pos = mgmt->u.auth.variable; - if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) - == ParseFailed) { - wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)"); - return; - } - if (elems.challenge == NULL) { - wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key " - "auth frame"); - return; - } - ieee80211_send_auth(wpa_s, 3, elems.challenge - 2, - elems.challenge_len + 2, 1); -} - - -static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - struct wpa_ssid *ssid = wpa_s->current_ssid; - u16 auth_alg, auth_transaction, status_code; - int adhoc; - - adhoc = ssid && ssid->mode == WPAS_MODE_IBSS; - - if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) { - wpa_printf(MSG_DEBUG, "MLME: authentication frame received " - "from " MACSTR ", but not in authenticate state - " - "ignored", MAC2STR(mgmt->sa)); - return; - } - - if (len < 24 + 6) { - wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication " - "frame received from " MACSTR " - ignored", - (unsigned long) len, MAC2STR(mgmt->sa)); - return; - } - - if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: authentication frame received " - "from unknown AP (SA=" MACSTR " BSSID=" MACSTR - ") - ignored", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: authentication frame received " - "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR - ") - ignored", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - - wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR - " (alg=%d transaction=%d status=%d)", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code); - - if (adhoc) { - /* IEEE 802.11 standard does not require authentication in IBSS - * networks and most implementations do not seem to use it. - * However, try to reply to authentication attempts if someone - * has actually implemented this. - * TODO: Could implement shared key authentication. */ - if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { - wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS " - "authentication frame (alg=%d " - "transaction=%d)", - auth_alg, auth_transaction); - return; - } - ieee80211_send_auth(wpa_s, 2, NULL, 0, 0); - } - - if (auth_alg != wpa_s->mlme.auth_alg || - auth_transaction != wpa_s->mlme.auth_transaction) { - wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame " - "(alg=%d transaction=%d)", - auth_alg, auth_transaction); - return; - } - - if (status_code != WLAN_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, "MLME: AP denied authentication " - "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg, - status_code); - if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - const int num_algs = 3; - u8 algs[num_algs]; - int i, pos; - algs[0] = algs[1] = algs[2] = 0xff; - if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN) - algs[0] = WLAN_AUTH_OPEN; - if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED) - algs[1] = WLAN_AUTH_SHARED_KEY; - if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP) - algs[2] = WLAN_AUTH_LEAP; - if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN) - pos = 0; - else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY) - pos = 1; - else - pos = 2; - for (i = 0; i < num_algs; i++) { - pos++; - if (pos >= num_algs) - pos = 0; - if (algs[pos] == wpa_s->mlme.auth_alg || - algs[pos] == 0xff) - continue; - if (algs[pos] == WLAN_AUTH_SHARED_KEY && - !ieee80211_sta_wep_configured(wpa_s)) - continue; - wpa_s->mlme.auth_alg = algs[pos]; - wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d " - "for next try", - wpa_s->mlme.auth_alg); - break; - } - } - return; - } - - switch (wpa_s->mlme.auth_alg) { - case WLAN_AUTH_OPEN: - case WLAN_AUTH_LEAP: - ieee80211_auth_completed(wpa_s); - break; - case WLAN_AUTH_SHARED_KEY: - if (wpa_s->mlme.auth_transaction == 4) - ieee80211_auth_completed(wpa_s); - else - ieee80211_auth_challenge(wpa_s, mgmt, len, - rx_status); - break; -#ifdef CONFIG_IEEE80211R - case WLAN_AUTH_FT: - { - union wpa_event_data data; - struct wpabuf *ric = NULL; - os_memset(&data, 0, sizeof(data)); - data.ft_ies.ies = mgmt->u.auth.variable; - data.ft_ies.ies_len = len - - (mgmt->u.auth.variable - (u8 *) mgmt); - os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN); - if (os_strcmp(wpa_s->driver->name, "test") == 0 && - wpa_s->mlme.wmm_enabled) { - ric = wpabuf_alloc(200); - if (ric) { - /* Build simple RIC-Request: RDIE | TSPEC */ - - /* RIC Data (RDIE) */ - wpabuf_put_u8(ric, WLAN_EID_RIC_DATA); - wpabuf_put_u8(ric, 4); - wpabuf_put_u8(ric, 0); /* RDIE Identifier */ - wpabuf_put_u8(ric, 1); /* Resource Descriptor - * Count */ - wpabuf_put_le16(ric, 0); /* Status Code */ - - /* WMM TSPEC */ - ieee80211_build_tspec(ric); - - data.ft_ies.ric_ies = wpabuf_head(ric); - data.ft_ies.ric_ies_len = wpabuf_len(ric); - } - } - - wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); - wpabuf_free(ric); - ieee80211_auth_completed(wpa_s); - break; - } -#endif /* CONFIG_IEEE80211R */ - } -} - - -static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - u16 reason_code; - - if (len < 24 + 2) { - wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication " - "frame received from " MACSTR " - ignored", - (unsigned long) len, MAC2STR(mgmt->sa)); - return; - } - - if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received " - "from unknown AP (SA=" MACSTR " BSSID=" MACSTR - ") - ignored", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - reason_code = le_to_host16(mgmt->u.deauth.reason_code); - - wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR - " (reason=%d)", MAC2STR(mgmt->sa), reason_code); - - if (wpa_s->mlme.authenticated) - wpa_printf(MSG_DEBUG, "MLME: deauthenticated"); - - if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE || - wpa_s->mlme.state == IEEE80211_ASSOCIATE || - wpa_s->mlme.state == IEEE80211_ASSOCIATED) { - wpa_s->mlme.state = IEEE80211_AUTHENTICATE; - ieee80211_reschedule_timer(wpa_s, - IEEE80211_RETRY_AUTH_INTERVAL); - } - - ieee80211_set_associated(wpa_s, 0); - wpa_s->mlme.authenticated = 0; -} - - -static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - u16 reason_code; - - if (len < 24 + 2) { - wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation " - "frame received from " MACSTR " - ignored", - (unsigned long) len, MAC2STR(mgmt->sa)); - return; - } - - if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: disassociation frame received " - "from unknown AP (SA=" MACSTR " BSSID=" MACSTR - ") - ignored", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - reason_code = le_to_host16(mgmt->u.disassoc.reason_code); - - wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR - " (reason=%d)", MAC2STR(mgmt->sa), reason_code); - - if (wpa_s->mlme.associated) - wpa_printf(MSG_DEBUG, "MLME: disassociated"); - - if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) { - wpa_s->mlme.state = IEEE80211_ASSOCIATE; - ieee80211_reschedule_timer(wpa_s, - IEEE80211_RETRY_AUTH_INTERVAL); - } - - ieee80211_set_associated(wpa_s, 0); -} - - -static void ieee80211_build_tspec(struct wpabuf *buf) -{ - struct wmm_tspec_element *tspec; - int tid, up; - - tspec = wpabuf_put(buf, sizeof(*tspec)); - tspec->eid = WLAN_EID_VENDOR_SPECIFIC; - tspec->length = sizeof(*tspec) - 2; - tspec->oui[0] = 0x00; - tspec->oui[1] = 0x50; - tspec->oui[2] = 0xf2; - tspec->oui_type = 2; - tspec->oui_subtype = 2; - tspec->version = 1; - - tid = 1; - up = 6; /* Voice */ - tspec->ts_info[0] = (tid << 1) | - (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) | - BIT(7); - tspec->ts_info[1] = up << 3; - tspec->nominal_msdu_size = host_to_le16(1530); - tspec->mean_data_rate = host_to_le32(128000); /* bits per second */ - tspec->minimum_phy_rate = host_to_le32(6000000); - tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */ -} - - -static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) -{ - struct wpabuf *buf; - struct ieee80211_mgmt *mgmt; - size_t alen; - - wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC"); - mgmt = NULL; - alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; - - buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element)); - if (buf == NULL) - return; - - mgmt = wpabuf_put(buf, alen); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_WMM; - mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ; - mgmt->u.action.u.wmm_action.dialog_token = 1; - mgmt->u.action.u.wmm_action.status_code = 0; - - ieee80211_build_tspec(buf); - - ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); - wpabuf_free(buf); -} - - -static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status, - int reassoc) -{ - u8 rates[32]; - size_t rates_len; - u16 capab_info, status_code, aid; - struct ieee802_11_elems elems; - u8 *pos; - - /* AssocResp and ReassocResp have identical structure, so process both - * of them in this function. */ - - if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) { - wpa_printf(MSG_DEBUG, "MLME: association frame received from " - MACSTR ", but not in associate state - ignored", - MAC2STR(mgmt->sa)); - return; - } - - if (len < 24 + 6) { - wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association " - "frame received from " MACSTR " - ignored", - (unsigned long) len, MAC2STR(mgmt->sa)); - return; - } - - if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: association frame received from " - "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " - "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info); - status_code = le_to_host16(mgmt->u.assoc_resp.status_code); - aid = le_to_host16(mgmt->u.assoc_resp.aid); - if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) - wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 " - "not set", aid); - aid &= ~(BIT(15) | BIT(14)); - - wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR - " (capab=0x%x status=%d aid=%d)", - reassoc ? "Rea" : "A", MAC2STR(mgmt->sa), - capab_info, status_code, aid); - - pos = mgmt->u.assoc_resp.variable; - if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0) - == ParseFailed) { - wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp"); - return; - } - - if (status_code != WLAN_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)", - status_code); -#ifdef CONFIG_IEEE80211W - if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && - elems.timeout_int && elems.timeout_int_len == 5 && - elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { - u32 tu, ms; - tu = WPA_GET_LE32(elems.timeout_int + 1); - ms = tu * 1024 / 1000; - wpa_printf(MSG_DEBUG, "MLME: AP rejected association " - "temporarily; comeback duration %u TU " - "(%u ms)", tu, ms); - if (ms > IEEE80211_ASSOC_TIMEOUT) { - wpa_printf(MSG_DEBUG, "MLME: Update timer " - "based on comeback duration"); - ieee80211_reschedule_timer(wpa_s, ms); - } - } -#endif /* CONFIG_IEEE80211W */ - return; - } - - if (elems.supp_rates == NULL) { - wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in " - "AssocResp"); - return; - } - - if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) { - if (!reassoc) { - wpa_printf(MSG_DEBUG, "MLME: AP tried to use " - "association, not reassociation, response " - "with FT"); - return; - } - if (wpa_ft_validate_reassoc_resp( - wpa_s->wpa, pos, len - (pos - (u8 *) mgmt), - mgmt->sa) < 0) { - wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc" - "Resp failed"); - return; - } - } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos, - len - (pos - (u8 *) mgmt)) < 0) - return; - - wpa_printf(MSG_DEBUG, "MLME: associated"); - wpa_s->mlme.aid = aid; - wpa_s->mlme.ap_capab = capab_info; - - os_free(wpa_s->mlme.assocresp_ies); - wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt); - wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len); - if (wpa_s->mlme.assocresp_ies) { - os_memcpy(wpa_s->mlme.assocresp_ies, pos, - wpa_s->mlme.assocresp_ies_len); - } - - ieee80211_set_associated(wpa_s, 1); - - rates_len = elems.supp_rates_len; - if (rates_len > sizeof(rates)) - rates_len = sizeof(rates); - os_memcpy(rates, elems.supp_rates, rates_len); - if (elems.ext_supp_rates) { - size_t _len = elems.ext_supp_rates_len; - if (_len > sizeof(rates) - rates_len) - _len = sizeof(rates) - rates_len; - os_memcpy(rates + rates_len, elems.ext_supp_rates, _len); - rates_len += _len; - } - - if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) { - wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the " - "netstack"); - } - if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) < - 0) { - wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the " - "netstack"); - } - - /* Remove STA entry before adding a new one just in case to avoid - * problems with existing configuration (e.g., keys). */ - wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid); - if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) { - wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the " - "netstack"); - } - - if (elems.wmm && wpa_s->mlme.wmm_enabled) - ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len); - - ieee80211_associated(wpa_s); - - if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT && - os_strcmp(wpa_s->driver->name, "test") == 0 && - elems.wmm && wpa_s->mlme.wmm_enabled) { - /* Test WMM-AC - send ADDTS for WMM TSPEC */ - ieee80211_tx_addts(wpa_s); - } -} - - -/* Caller must hold local->sta_bss_lock */ -static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s, - struct ieee80211_sta_bss *bss) -{ - bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; - wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss; -} - - -/* Caller must hold local->sta_bss_lock */ -static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s, - struct ieee80211_sta_bss *bss) -{ - struct ieee80211_sta_bss *b, *prev = NULL; - b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]; - while (b) { - if (b == bss) { - if (prev == NULL) { - wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] - = bss->hnext; - } else { - prev->hnext = bss->hnext; - } - break; - } - prev = b; - b = b->hnext; - } -} - - -static struct ieee80211_sta_bss * -ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid) -{ - struct ieee80211_sta_bss *bss; - - bss = os_zalloc(sizeof(*bss)); - if (bss == NULL) - return NULL; - os_memcpy(bss->bssid, bssid, ETH_ALEN); - - /* TODO: order by RSSI? */ - bss->next = wpa_s->mlme.sta_bss_list; - wpa_s->mlme.sta_bss_list = bss; - __ieee80211_bss_hash_add(wpa_s, bss); - return bss; -} - - -static struct ieee80211_sta_bss * -ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid) -{ - struct ieee80211_sta_bss *bss; - - bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)]; - while (bss) { - if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) - break; - bss = bss->hnext; - } - return bss; -} - - -static void ieee80211_bss_free(struct wpa_supplicant *wpa_s, - struct ieee80211_sta_bss *bss) -{ - __ieee80211_bss_hash_del(wpa_s, bss); - os_free(bss->ie); - os_free(bss->wpa_ie); - os_free(bss->rsn_ie); - os_free(bss->wmm_ie); - os_free(bss->mdie); - os_free(bss); -} - - -static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s) -{ - struct ieee80211_sta_bss *bss, *prev; - - bss = wpa_s->mlme.sta_bss_list; - wpa_s->mlme.sta_bss_list = NULL; - while (bss) { - prev = bss; - bss = bss->next; - ieee80211_bss_free(wpa_s, prev); - } -} - - -static void ieee80211_bss_info(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status, - int beacon) -{ - struct ieee802_11_elems elems; - size_t baselen; - int channel, invalid = 0, clen; - struct ieee80211_sta_bss *bss; - u64 timestamp; - u8 *pos, *ie_pos; - size_t ie_len; - - if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN)) - return; /* ignore ProbeResp to foreign address */ - -#if 0 - wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR, - beacon ? "Beacon" : "Probe Response", - MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); -#endif - - baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; - if (baselen > len) - return; - - pos = mgmt->u.beacon.timestamp; - timestamp = WPA_GET_LE64(pos); - -#if 0 /* FIX */ - if (local->conf.mode == IW_MODE_ADHOC && beacon && - os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) { -#ifdef IEEE80211_IBSS_DEBUG - static unsigned long last_tsf_debug = 0; - u64 tsf; - if (local->hw->get_tsf) - tsf = local->hw->get_tsf(local->mdev); - else - tsf = -1LLU; - if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { - wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID=" - MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld " - "@%ld", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), - tsf, timestamp, tsf - timestamp, jiffies); - last_tsf_debug = jiffies; - } -#endif /* IEEE80211_IBSS_DEBUG */ - } -#endif - - ie_pos = mgmt->u.beacon.variable; - ie_len = len - baselen; - if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed) - invalid = 1; - -#if 0 /* FIX */ - if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates && - os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 && - (sta = sta_info_get(local, mgmt->sa))) { - struct ieee80211_rate *rates; - size_t num_rates; - u32 supp_rates, prev_rates; - int i, j, oper_mode; - - rates = local->curr_rates; - num_rates = local->num_curr_rates; - oper_mode = wpa_s->mlme.sta_scanning ? - local->scan_oper_phymode : local->conf.phymode; - for (i = 0; i < local->hw->num_modes; i++) { - struct ieee80211_hw_modes *mode = &local->hw->modes[i]; - if (oper_mode == mode->mode) { - rates = mode->rates; - num_rates = mode->num_rates; - break; - } - } - - supp_rates = 0; - for (i = 0; i < elems.supp_rates_len + - elems.ext_supp_rates_len; i++) { - u8 rate = 0; - int own_rate; - if (i < elems.supp_rates_len) - rate = elems.supp_rates[i]; - else if (elems.ext_supp_rates) - rate = elems.ext_supp_rates - [i - elems.supp_rates_len]; - own_rate = 5 * (rate & 0x7f); - if (oper_mode == MODE_ATHEROS_TURBO) - own_rate *= 2; - for (j = 0; j < num_rates; j++) - if (rates[j].rate == own_rate) - supp_rates |= BIT(j); - } - - prev_rates = sta->supp_rates; - sta->supp_rates &= supp_rates; - if (sta->supp_rates == 0) { - /* No matching rates - this should not really happen. - * Make sure that at least one rate is marked - * supported to avoid issues with TX rate ctrl. */ - sta->supp_rates = wpa_s->mlme.supp_rates_bits; - } - if (sta->supp_rates != prev_rates) { - wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set " - "for " MACSTR " based on beacon info " - "(0x%x & 0x%x -> 0x%x)", - MAC2STR(sta->addr), prev_rates, - supp_rates, sta->supp_rates); - } - sta_info_release(local, sta); - } -#endif - - if (elems.ssid == NULL) - return; - - if (elems.ds_params && elems.ds_params_len == 1) - channel = elems.ds_params[0]; - else - channel = rx_status->channel; - - bss = ieee80211_bss_get(wpa_s, mgmt->bssid); - if (bss == NULL) { - bss = ieee80211_bss_add(wpa_s, mgmt->bssid); - if (bss == NULL) - return; - } else { -#if 0 - /* TODO: order by RSSI? */ - spin_lock_bh(&local->sta_bss_lock); - list_move_tail(&bss->list, &local->sta_bss_list); - spin_unlock_bh(&local->sta_bss_lock); -#endif - } - - if (bss->probe_resp && beacon) { - /* Do not allow beacon to override data from Probe Response. */ - return; - } - - bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); - bss->capability = le_to_host16(mgmt->u.beacon.capab_info); - - if (bss->ie == NULL || bss->ie_len < ie_len) { - os_free(bss->ie); - bss->ie = os_malloc(ie_len); - } - if (bss->ie) { - os_memcpy(bss->ie, ie_pos, ie_len); - bss->ie_len = ie_len; - } - - if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) { - os_memcpy(bss->ssid, elems.ssid, elems.ssid_len); - bss->ssid_len = elems.ssid_len; - } - - bss->supp_rates_len = 0; - if (elems.supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; - if (clen > elems.supp_rates_len) - clen = elems.supp_rates_len; - os_memcpy(&bss->supp_rates[bss->supp_rates_len], - elems.supp_rates, clen); - bss->supp_rates_len += clen; - } - if (elems.ext_supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; - if (clen > elems.ext_supp_rates_len) - clen = elems.ext_supp_rates_len; - os_memcpy(&bss->supp_rates[bss->supp_rates_len], - elems.ext_supp_rates, clen); - bss->supp_rates_len += clen; - } - - if (elems.wpa_ie && - (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len || - os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) { - os_free(bss->wpa_ie); - bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2); - if (bss->wpa_ie) { - os_memcpy(bss->wpa_ie, elems.wpa_ie - 2, - elems.wpa_ie_len + 2); - bss->wpa_ie_len = elems.wpa_ie_len + 2; - } else - bss->wpa_ie_len = 0; - } else if (!elems.wpa_ie && bss->wpa_ie) { - os_free(bss->wpa_ie); - bss->wpa_ie = NULL; - bss->wpa_ie_len = 0; - } - - if (elems.rsn_ie && - (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len || - os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) { - os_free(bss->rsn_ie); - bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2); - if (bss->rsn_ie) { - os_memcpy(bss->rsn_ie, elems.rsn_ie - 2, - elems.rsn_ie_len + 2); - bss->rsn_ie_len = elems.rsn_ie_len + 2; - } else - bss->rsn_ie_len = 0; - } else if (!elems.rsn_ie && bss->rsn_ie) { - os_free(bss->rsn_ie); - bss->rsn_ie = NULL; - bss->rsn_ie_len = 0; - } - - if (elems.wmm && - (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len || - os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) { - os_free(bss->wmm_ie); - bss->wmm_ie = os_malloc(elems.wmm_len + 2); - if (bss->wmm_ie) { - os_memcpy(bss->wmm_ie, elems.wmm - 2, - elems.wmm_len + 2); - bss->wmm_ie_len = elems.wmm_len + 2; - } else - bss->wmm_ie_len = 0; - } else if (!elems.wmm && bss->wmm_ie) { - os_free(bss->wmm_ie); - bss->wmm_ie = NULL; - bss->wmm_ie_len = 0; - } - -#ifdef CONFIG_IEEE80211R - if (elems.mdie && - (bss->mdie == NULL || bss->mdie_len != elems.mdie_len || - os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) { - os_free(bss->mdie); - bss->mdie = os_malloc(elems.mdie_len + 2); - if (bss->mdie) { - os_memcpy(bss->mdie, elems.mdie - 2, - elems.mdie_len + 2); - bss->mdie_len = elems.mdie_len + 2; - } else - bss->mdie_len = 0; - } else if (!elems.mdie && bss->mdie) { - os_free(bss->mdie); - bss->mdie = NULL; - bss->mdie_len = 0; - } -#endif /* CONFIG_IEEE80211R */ - - bss->hw_mode = wpa_s->mlme.phymode; - bss->channel = channel; - bss->freq = wpa_s->mlme.freq; - if (channel != wpa_s->mlme.channel && - (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G || - wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) && - channel >= 1 && channel <= 14) { - static const int freq_list[] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 - }; - /* IEEE 802.11g/b mode can receive packets from neighboring - * channels, so map the channel into frequency. */ - bss->freq = freq_list[channel - 1]; - } - bss->timestamp = timestamp; - os_get_time(&bss->last_update); - bss->rssi = rx_status->ssi; - if (!beacon) - bss->probe_resp++; -} - - -static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0); -} - - -static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - int use_protection; - size_t baselen; - struct ieee802_11_elems elems; - - ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1); - - if (!wpa_s->mlme.associated || - os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) - return; - - /* Process beacon from the current BSS */ - baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; - if (baselen > len) - return; - - if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, - &elems, 0) == ParseFailed) - return; - - use_protection = 0; - if (elems.erp_info && elems.erp_info_len >= 1) { - use_protection = - (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; - } - - if (use_protection != !!wpa_s->mlme.use_protection) { - wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR - ")", - use_protection ? "enabled" : "disabled", - MAC2STR(wpa_s->bssid)); - wpa_s->mlme.use_protection = use_protection ? 1 : 0; - wpa_s->mlme.cts_protect_erp_frames = use_protection; - } - - if (elems.wmm && wpa_s->mlme.wmm_enabled) { - ieee80211_sta_wmm_params(wpa_s, elems.wmm, - elems.wmm_len); - } -} - - -static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - int tx_last_beacon, adhoc; -#if 0 /* FIX */ - struct ieee80211_mgmt *resp; -#endif - u8 *pos, *end; - struct wpa_ssid *ssid = wpa_s->current_ssid; - - adhoc = ssid && ssid->mode == WPAS_MODE_IBSS; - - if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED || - len < 24 + 2 || wpa_s->mlme.probe_resp == NULL) - return; - -#if 0 /* FIX */ - if (local->hw->tx_last_beacon) - tx_last_beacon = local->hw->tx_last_beacon(local->mdev); - else -#endif - tx_last_beacon = 1; - -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR - " BSSID=" MACSTR " (tx_last_beacon=%d)", - MAC2STR(mgmt->sa), MAC2STR(mgmt->da), - MAC2STR(mgmt->bssid), tx_last_beacon); -#endif /* IEEE80211_IBSS_DEBUG */ - - if (!tx_last_beacon) - return; - - if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 && - os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) - return; - - end = ((u8 *) mgmt) + len; - pos = mgmt->u.probe_req.variable; - if (pos[0] != WLAN_EID_SSID || - pos + 2 + pos[1] > end) { - wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from " - MACSTR, MAC2STR(mgmt->sa)); - return; - } - if (pos[1] != 0 && - (pos[1] != wpa_s->mlme.ssid_len || - os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0)) - { - /* Ignore ProbeReq for foreign SSID */ - return; - } - -#if 0 /* FIX */ - /* Reply with ProbeResp */ - skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC); - if (skb == NULL) - return; - - resp = (struct ieee80211_mgmt *) skb->data; - os_memcpy(resp->da, mgmt->sa, ETH_ALEN); -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR, - MAC2STR(resp->da)); -#endif /* IEEE80211_IBSS_DEBUG */ - ieee80211_sta_tx(wpa_s, skb, 0, 1); -#endif -} - - -#ifdef CONFIG_IEEE80211R -static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - union wpa_event_data data; - u16 status; - u8 *sta_addr, *target_ap_addr; - - if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) { - wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame"); - return; - } - - /* - * Only FT Action Response is needed for now since reservation - * protocol is not supported. - */ - if (mgmt->u.action.u.ft_action_resp.action != 2) { - wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d", - mgmt->u.action.u.ft_action_resp.action); - return; - } - - status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code); - sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr; - target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr; - wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR - " TargetAP " MACSTR " Status Code %d", - MAC2STR(sta_addr), MAC2STR(target_ap_addr), status); - if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR - " in FT Action Response", MAC2STR(sta_addr)); - return; - } - - if (status) { - wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates " - "failure (status code %d)", status); - /* TODO: report error to FT code(?) */ - return; - } - - os_memset(&data, 0, sizeof(data)); - data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable; - data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable - - (u8 *) mgmt); - data.ft_ies.ft_action = 1; - os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN); - wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); - /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed - * successfully */ - wpa_s->mlme.prev_bssid_set = 1; - wpa_s->mlme.auth_alg = WLAN_AUTH_FT; - os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN); - os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN); - ieee80211_associate(wpa_s); -} -#endif /* CONFIG_IEEE80211R */ - - -#ifdef CONFIG_IEEE80211W - -/* MLME-SAQuery.response */ -static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s, - const u8 *addr, const u8 *trans_id) -{ - struct ieee80211_mgmt *mgmt; - int res; - size_t len; - - mgmt = os_zalloc(sizeof(*mgmt)); - if (mgmt == NULL) { - wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " - "SA Query action frame"); - return -1; - } - - len = 24; - os_memcpy(mgmt->da, addr, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_SA_QUERY; - mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id, - WLAN_SA_QUERY_TR_ID_LEN); - len += 1 + sizeof(mgmt->u.action.u.sa_query_resp); - - res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len); - os_free(mgmt); - - return res; -} - - -static void ieee80211_rx_mgmt_sa_query_action( - struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, - struct ieee80211_rx_status *rx_status) -{ - if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) { - wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame"); - return; - } - - if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) { - wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d", - mgmt->u.action.u.sa_query_req.action); - return; - } - - if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown " - "source " MACSTR, MAC2STR(mgmt->sa)); - return; - } - - if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) { - wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during " - "association process"); - return; - } - - wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request"); - ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u. - sa_query_req.trans_id); -} - -#endif /* CONFIG_IEEE80211W */ - - -static void dump_tspec(struct wmm_tspec_element *tspec) -{ - int up, psb, dir, tid; - u16 val; - - up = (tspec->ts_info[1] >> 3) & 0x07; - psb = (tspec->ts_info[1] >> 2) & 0x01; - dir = (tspec->ts_info[0] >> 5) & 0x03; - tid = (tspec->ts_info[0] >> 1) & 0x0f; - wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", - up, psb, dir, tid); - val = le_to_host16(tspec->nominal_msdu_size); - wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", - val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); - wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", - le_to_host32(tspec->mean_data_rate)); - wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", - le_to_host32(tspec->minimum_phy_rate)); - val = le_to_host16(tspec->surplus_bandwidth_allowance); - wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", - val >> 13, 10000 * (val & 0x1fff) / 0x2000); - val = le_to_host16(tspec->medium_time); - wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)", - val, 32 * val); -} - - -static int is_wmm_tspec(const u8 *ie, size_t len) -{ - const struct wmm_tspec_element *tspec; - - if (len < sizeof(*tspec)) - return 0; - - tspec = (const struct wmm_tspec_element *) ie; - if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC || - tspec->length < sizeof(*tspec) - 2 || - tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 || - tspec->oui[2] != 0xf2 || tspec->oui_type != 2 || - tspec->oui_subtype != 2 || tspec->version != 1) - return 0; - - return 1; -} - - -static void ieee80211_rx_addts_resp( - struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, - size_t var_len) -{ - struct wmm_tspec_element *tspec; - - wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response"); - wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)", - mgmt->u.action.u.wmm_action.variable, var_len); - if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) - return; - tspec = (struct wmm_tspec_element *) - mgmt->u.action.u.wmm_action.variable; - dump_tspec(tspec); -} - - -static void ieee80211_rx_delts( - struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, - size_t var_len) -{ - struct wmm_tspec_element *tspec; - - wpa_printf(MSG_DEBUG, "WMM: Received DELTS"); - wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)", - mgmt->u.action.u.wmm_action.variable, var_len); - if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len)) - return; - tspec = (struct wmm_tspec_element *) - mgmt->u.action.u.wmm_action.variable; - dump_tspec(tspec); -} - - -static void ieee80211_rx_mgmt_wmm_action( - struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len, - struct ieee80211_rx_status *rx_status) -{ - size_t alen; - - alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; - if (len < alen) { - wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short"); - return; - } - - wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, " - "Dialog Token %d, Status Code %d", - mgmt->u.action.u.wmm_action.action_code, - mgmt->u.action.u.wmm_action.dialog_token, - mgmt->u.action.u.wmm_action.status_code); - - switch (mgmt->u.action.u.wmm_action.action_code) { - case WMM_ACTION_CODE_ADDTS_RESP: - ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen); - break; - case WMM_ACTION_CODE_DELTS: - ieee80211_rx_delts(wpa_s, mgmt, len, len - alen); - break; - default: - wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d", - mgmt->u.action.u.wmm_action.action_code); - break; - } -} - - -static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - wpa_printf(MSG_DEBUG, "MLME: received Action frame"); - - if (len < 25) - return; - - switch (mgmt->u.action.category) { -#ifdef CONFIG_IEEE80211R - case WLAN_ACTION_FT: - ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status); - break; -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - case WLAN_ACTION_SA_QUERY: - ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status); - break; -#endif /* CONFIG_IEEE80211W */ - case WLAN_ACTION_WMM: - ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status); - break; - case WLAN_ACTION_PUBLIC: - if (wpa_s->mlme.public_action_cb) { - wpa_s->mlme.public_action_cb( - wpa_s->mlme.public_action_cb_ctx, - (u8 *) mgmt, len, rx_status->freq); - return; - } - break; - default: - wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d", - mgmt->u.action.category); - break; - } -} - - -static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s, - const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status) -{ - struct ieee80211_mgmt *mgmt; - u16 fc; - - if (len < 24) - return; - - mgmt = (struct ieee80211_mgmt *) buf; - fc = le_to_host16(mgmt->frame_control); - - switch (WLAN_FC_GET_STYPE(fc)) { - case WLAN_FC_STYPE_PROBE_REQ: - ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_AUTH: - ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_ASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0); - break; - case WLAN_FC_STYPE_REASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1); - break; - case WLAN_FC_STYPE_DEAUTH: - ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_DISASSOC: - ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status); - break; - case WLAN_FC_STYPE_ACTION: - ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status); - break; - default: - wpa_printf(MSG_DEBUG, "MLME: received unknown management " - "frame - stype=%d", WLAN_FC_GET_STYPE(fc)); - break; - } -} - - -static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s, - const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status) -{ - struct ieee80211_mgmt *mgmt; - u16 fc; - - if (len < 24) - return; - - mgmt = (struct ieee80211_mgmt *) buf; - fc = le_to_host16(mgmt->frame_control); - - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) { - if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { - ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, - len, rx_status); - } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { - ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status); - } - } -} - - -static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s) -{ - int active = 0; - -#if 0 /* FIX */ - list_for_each(ptr, &local->sta_list) { - sta = list_entry(ptr, struct sta_info, list); - if (sta->dev == dev && - time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, - jiffies)) { - active++; - break; - } - } -#endif - - return active; -} - - -static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s) -{ -#if 0 /* FIX */ - list_for_each_safe(ptr, n, &local->sta_list) { - sta = list_entry(ptr, struct sta_info, list); - if (time_after(jiffies, sta->last_rx + - IEEE80211_IBSS_INACTIVITY_LIMIT)) { - wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA " - MACSTR, MAC2STR(sta->addr)); - sta_info_free(local, sta, 1); - } - } -#endif -} - - -static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s) -{ - struct wpa_driver_scan_params params; - - ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); - - ieee80211_sta_expire(wpa_s); - if (ieee80211_sta_active_ibss(wpa_s)) - return; - - wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for " - "other IBSS networks with same SSID (merge)"); - os_memset(¶ms, 0, sizeof(params)); - params.ssids[0].ssid = wpa_s->mlme.ssid; - params.ssids[0].ssid_len = wpa_s->mlme.ssid_len; - params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0; - ieee80211_sta_req_scan(wpa_s, ¶ms); -} - - -static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - - switch (wpa_s->mlme.state) { - case IEEE80211_DISABLED: - break; - case IEEE80211_AUTHENTICATE: - ieee80211_authenticate(wpa_s); - break; - case IEEE80211_ASSOCIATE: - ieee80211_associate(wpa_s); - break; - case IEEE80211_ASSOCIATED: - ieee80211_associated(wpa_s); - break; - case IEEE80211_IBSS_SEARCH: - ieee80211_sta_find_ibss(wpa_s); - break; - case IEEE80211_IBSS_JOINED: - ieee80211_sta_merge_ibss(wpa_s); - break; - default: - wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d", - wpa_s->mlme.state); - break; - } - - if (ieee80211_privacy_mismatch(wpa_s)) { - wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch " - "and mixed-cell disabled - disassociate"); - - ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED); - ieee80211_set_associated(wpa_s, 0); - } -} - - -static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s) -{ - struct wpa_ssid *ssid = wpa_s->current_ssid; - if (ssid && ssid->mode != WPAS_MODE_INFRA) - return; - -#if 0 /* FIX */ - if (local->hw->reset_tsf) { - /* Reset own TSF to allow time synchronization work. */ - local->hw->reset_tsf(local->mdev); - } -#endif - - wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */ - - - if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN) - wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; - else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED) - wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY; - else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP) - wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP; - else - wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN; - wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d", - wpa_s->mlme.auth_alg); - wpa_s->mlme.auth_transaction = -1; - wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0; - ieee80211_authenticate(wpa_s); -} - - -static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s) -{ -#if 0 /* FIX */ - int m, c; - - for (m = 0; m < local->hw->num_modes; m++) { - struct ieee80211_hw_modes *mode = &local->hw->modes[m]; - if (mode->mode != local->conf.phymode) - continue; - for (c = 0; c < mode->num_channels; c++) { - struct ieee80211_channel *chan = &mode->channels[c]; - if (chan->flag & IEEE80211_CHAN_W_SCAN && - chan->chan == local->conf.channel) { - if (chan->flag & IEEE80211_CHAN_W_IBSS) - return 1; - break; - } - } - } -#endif - - return 0; -} - - -static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s, - struct ieee80211_sta_bss *bss) -{ - int res = 0, rates, done = 0, bssid_changed; - struct ieee80211_mgmt *mgmt; -#if 0 /* FIX */ - struct ieee80211_tx_control control; - struct ieee80211_rate *rate; - struct rate_control_extra extra; -#endif - u8 *pos, *buf; - size_t len; - - /* Remove possible STA entries from other IBSS networks. */ -#if 0 /* FIX */ - sta_info_flush(local, NULL); - - if (local->hw->reset_tsf) { - /* Reset own TSF to allow time synchronization work. */ - local->hw->reset_tsf(local->mdev); - } -#endif - bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN); - os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); - -#if 0 /* FIX */ - local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; - - sdata->drop_unencrypted = bss->capability & - host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0; -#endif - -#if 0 /* FIX */ - os_memset(&rq, 0, sizeof(rq)); - rq.m = bss->freq * 100000; - rq.e = 1; - res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL); -#endif - - if (!ieee80211_ibss_allowed(wpa_s)) { -#if 0 /* FIX */ - wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d " - "(%d MHz)", local->conf.channel, - local->conf.freq); -#endif - return -1; - } - - /* Set beacon template based on scan results */ - buf = os_malloc(400); - len = 0; - do { - if (buf == NULL) - break; - - mgmt = (struct ieee80211_mgmt *) buf; - len += 24 + sizeof(mgmt->u.beacon); - os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_BEACON); - os_memset(mgmt->da, 0xff, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); -#if 0 /* FIX */ - mgmt->u.beacon.beacon_int = - host_to_le16(local->conf.beacon_int); -#endif - mgmt->u.beacon.capab_info = host_to_le16(bss->capability); - - pos = buf + len; - len += 2 + wpa_s->mlme.ssid_len; - *pos++ = WLAN_EID_SSID; - *pos++ = wpa_s->mlme.ssid_len; - os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); - - rates = bss->supp_rates_len; - if (rates > 8) - rates = 8; - pos = buf + len; - len += 2 + rates; - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = rates; - os_memcpy(pos, bss->supp_rates, rates); - - pos = buf + len; - len += 2 + 1; - *pos++ = WLAN_EID_DS_PARAMS; - *pos++ = 1; - *pos++ = bss->channel; - - pos = buf + len; - len += 2 + 2; - *pos++ = WLAN_EID_IBSS_PARAMS; - *pos++ = 2; - /* FIX: set ATIM window based on scan results */ - *pos++ = 0; - *pos++ = 0; - - if (bss->supp_rates_len > 8) { - rates = bss->supp_rates_len - 8; - pos = buf + len; - len += 2 + rates; - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = rates; - os_memcpy(pos, &bss->supp_rates[8], rates); - } - -#if 0 /* FIX */ - os_memset(&control, 0, sizeof(control)); - control.pkt_type = PKT_PROBE_RESP; - os_memset(&extra, 0, sizeof(extra)); - extra.endidx = local->num_curr_rates; - rate = rate_control_get_rate(wpa_s, skb, &extra); - if (rate == NULL) { - wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX " - "rate for IBSS beacon"); - break; - } - control.tx_rate = (wpa_s->mlme.short_preamble && - (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? - rate->val2 : rate->val; - control.antenna_sel = local->conf.antenna_sel; - control.power_level = local->conf.power_level; - control.no_ack = 1; - control.retry_limit = 1; - control.rts_cts_duration = 0; -#endif - -#if 0 /* FIX */ - wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC); - if (wpa_s->mlme.probe_resp) { - mgmt = (struct ieee80211_mgmt *) - wpa_s->mlme.probe_resp->data; - mgmt->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_PROBE_RESP); - } else { - wpa_printf(MSG_DEBUG, "MLME: Could not allocate " - "ProbeResp template for IBSS"); - } - - if (local->hw->beacon_update && - local->hw->beacon_update(wpa_s, skb, &control) == 0) { - wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon " - "template based on scan results"); - skb = NULL; - } - - rates = 0; - for (i = 0; i < bss->supp_rates_len; i++) { - int rate = (bss->supp_rates[i] & 0x7f) * 5; - if (local->conf.phymode == MODE_ATHEROS_TURBO) - rate *= 2; - for (j = 0; j < local->num_curr_rates; j++) - if (local->curr_rates[j] == rate) - rates |= BIT(j); - } - wpa_s->mlme.supp_rates_bits = rates; -#endif - done = 1; - } while (0); - - os_free(buf); - if (!done) { - wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon " - "template"); - } - - wpa_s->mlme.state = IEEE80211_IBSS_JOINED; - ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL); - - return res; -} - - -#if 0 /* FIX */ -static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s) -{ - struct ieee80211_sta_bss *bss; - u8 bssid[ETH_ALEN], *pos; - int i; - -#if 0 - /* Easier testing, use fixed BSSID. */ - os_memset(bssid, 0xfe, ETH_ALEN); -#else - /* Generate random, not broadcast, locally administered BSSID. Mix in - * own MAC address to make sure that devices that do not have proper - * random number generator get different BSSID. */ - os_get_random(bssid, ETH_ALEN); - for (i = 0; i < ETH_ALEN; i++) - bssid[i] ^= wpa_s->own_addr[i]; - bssid[0] &= ~0x01; - bssid[0] |= 0x02; -#endif - - wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID " - MACSTR "", MAC2STR(bssid)); - - bss = ieee80211_bss_add(wpa_s, bssid); - if (bss == NULL) - return -ENOMEM; - -#if 0 /* FIX */ - if (local->conf.beacon_int == 0) - local->conf.beacon_int = 100; - bss->beacon_int = local->conf.beacon_int; - bss->hw_mode = local->conf.phymode; - bss->channel = local->conf.channel; - bss->freq = local->conf.freq; -#endif - os_get_time(&bss->last_update); - bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS); -#if 0 /* FIX */ - if (sdata->default_key) { - bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY); - } else - sdata->drop_unencrypted = 0; - bss->supp_rates_len = local->num_curr_rates; -#endif - pos = bss->supp_rates; -#if 0 /* FIX */ - for (i = 0; i < local->num_curr_rates; i++) { - int rate = local->curr_rates[i]; - if (local->conf.phymode == MODE_ATHEROS_TURBO) - rate /= 2; - *pos++ = (u8) (rate / 5); - } -#endif - - return ieee80211_sta_join_ibss(wpa_s, bss); -} -#endif - - -static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s) -{ - struct ieee80211_sta_bss *bss; - int found = 0; - u8 bssid[ETH_ALEN]; - int active_ibss; - struct os_time now; - - if (wpa_s->mlme.ssid_len == 0) - return -EINVAL; - - active_ibss = ieee80211_sta_active_ibss(wpa_s); -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)", - active_ibss); -#endif /* IEEE80211_IBSS_DEBUG */ - for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { - if (wpa_s->mlme.ssid_len != bss->ssid_len || - os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0 - || !(bss->capability & WLAN_CAPABILITY_IBSS)) - continue; -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found", - MAC2STR(bss->bssid)); -#endif /* IEEE80211_IBSS_DEBUG */ - os_memcpy(bssid, bss->bssid, ETH_ALEN); - found = 1; - if (active_ibss || - os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) - break; - } - -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current " - MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid)); -#endif /* IEEE80211_IBSS_DEBUG */ - if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 && - (bss = ieee80211_bss_get(wpa_s, bssid))) { - wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR - " based on configured SSID", - MAC2STR(bssid)); - return ieee80211_sta_join_ibss(wpa_s, bss); - } -#ifdef IEEE80211_IBSS_DEBUG - wpa_printf(MSG_DEBUG, " did not try to join ibss"); -#endif /* IEEE80211_IBSS_DEBUG */ - - /* Selected IBSS not found in current scan results - try to scan */ - os_get_time(&now); -#if 0 /* FIX */ - if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED && - !ieee80211_sta_active_ibss(wpa_s)) { - ieee80211_reschedule_timer(wpa_s, - IEEE80211_IBSS_MERGE_INTERVAL); - } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed + - IEEE80211_SCAN_INTERVAL)) { - wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS " - "to join"); - return ieee80211_sta_req_scan(wpa_s->mlme.ssid, - wpa_s->mlme.ssid_len); - } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) { - int interval = IEEE80211_SCAN_INTERVAL; - - if (time_after(jiffies, wpa_s->mlme.ibss_join_req + - IEEE80211_IBSS_JOIN_TIMEOUT)) { - if (wpa_s->mlme.create_ibss && - ieee80211_ibss_allowed(wpa_s)) - return ieee80211_sta_create_ibss(wpa_s); - if (wpa_s->mlme.create_ibss) { - wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed " - "on the configured channel %d " - "(%d MHz)", - local->conf.channel, - local->conf.freq); - } - - /* No IBSS found - decrease scan interval and continue - * scanning. */ - interval = IEEE80211_SCAN_INTERVAL_SLOW; - } - - wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; - ieee80211_reschedule_timer(wpa_s, interval); - return 0; - } -#endif - - return 0; -} - - -int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, - size_t *len) -{ - os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len); - *len = wpa_s->mlme.ssid_len; - return 0; -} - - -int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, - struct wpa_driver_associate_params *params) -{ - struct ieee80211_sta_bss *bss; - int bssid_changed; - - wpa_s->mlme.bssid_set = 0; - wpa_s->mlme.freq = params->freq; - if (params->bssid) { - bssid_changed = os_memcmp(wpa_s->bssid, params->bssid, - ETH_ALEN); - os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); - - if (!is_zero_ether_addr(params->bssid)) - wpa_s->mlme.bssid_set = 1; - bss = ieee80211_bss_get(wpa_s, wpa_s->bssid); - if (bss) { - wpa_s->mlme.phymode = bss->hw_mode; - wpa_s->mlme.channel = bss->channel; - wpa_s->mlme.freq = bss->freq; - } - } - -#if 0 /* FIX */ - /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is - * not defined. */ - if (local->hw->conf_tx) { - struct ieee80211_tx_queue_params qparam; - int i; - - os_memset(&qparam, 0, sizeof(qparam)); - /* TODO: are these ok defaults for all hw_modes? */ - qparam.aifs = 2; - qparam.cw_min = - local->conf.phymode == MODE_IEEE80211B ? 31 : 15; - qparam.cw_max = 1023; - qparam.burst_time = 0; - for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) - { - local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0, - &qparam); - } - /* IBSS uses different parameters for Beacon sending */ - qparam.cw_min++; - qparam.cw_min *= 2; - qparam.cw_min--; - local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam); - } -#endif - - if (wpa_s->mlme.ssid_len != params->ssid_len || - os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0) - wpa_s->mlme.prev_bssid_set = 0; - os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len); - os_memset(wpa_s->mlme.ssid + params->ssid_len, 0, - MAX_SSID_LEN - params->ssid_len); - wpa_s->mlme.ssid_len = params->ssid_len; - wpa_s->mlme.ssid_set = 1; - - os_free(wpa_s->mlme.extra_ie); - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - wpa_s->mlme.extra_ie = NULL; - wpa_s->mlme.extra_ie_len = 0; - } else { - wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len); - if (wpa_s->mlme.extra_ie == NULL) { - wpa_s->mlme.extra_ie_len = 0; - return -1; - } - os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie, - params->wpa_ie_len); - wpa_s->mlme.extra_ie_len = params->wpa_ie_len; - } - - wpa_s->mlme.key_mgmt = params->key_mgmt_suite; - - ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, - wpa_s->mlme.channel, wpa_s->mlme.freq); - - if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) { - os_get_time(&wpa_s->mlme.ibss_join_req); - wpa_s->mlme.state = IEEE80211_IBSS_SEARCH; - return ieee80211_sta_find_ibss(wpa_s); - } - - if (wpa_s->mlme.bssid_set) - ieee80211_sta_new_auth(wpa_s); - - return 0; -} - - -static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s) -{ - wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel; - wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq; - wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode; -} - - -static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s) -{ - wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel; - wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq; - wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode; - if (wpa_s->mlme.freq == 0) - return 0; - return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode, - wpa_s->mlme.channel, - wpa_s->mlme.freq); -} - - -static int ieee80211_active_scan(struct wpa_supplicant *wpa_s) -{ - size_t m; - int c; - - for (m = 0; m < wpa_s->mlme.num_modes; m++) { - struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m]; - if ((int) mode->mode != (int) wpa_s->mlme.phymode) - continue; - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - chan->chan == wpa_s->mlme.channel) { - if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN)) - return 1; - break; - } - } - } - - return 0; -} - - -static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - struct hostapd_hw_modes *mode; - struct hostapd_channel_data *chan; - int skip = 0; - int timeout = 0; - struct wpa_ssid *ssid = wpa_s->current_ssid; - int adhoc; - - if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL) - return; - - adhoc = ssid && ssid->mode == 1; - - switch (wpa_s->mlme.scan_state) { - case SCAN_SET_CHANNEL: - mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]; - if (wpa_s->mlme.scan_hw_mode_idx >= - (int) wpa_s->mlme.num_modes || - (wpa_s->mlme.scan_hw_mode_idx + 1 == - (int) wpa_s->mlme.num_modes - && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) { - if (ieee80211_sta_restore_oper_chan(wpa_s)) { - wpa_printf(MSG_DEBUG, "MLME: failed to " - "restore operational channel after " - "scan"); - } - wpa_printf(MSG_DEBUG, "MLME: scan completed"); - wpa_s->mlme.sta_scanning = 0; - os_get_time(&wpa_s->mlme.last_scan_completed); - wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL); - if (adhoc) { - if (!wpa_s->mlme.bssid_set || - (wpa_s->mlme.state == - IEEE80211_IBSS_JOINED && - !ieee80211_sta_active_ibss(wpa_s))) - ieee80211_sta_find_ibss(wpa_s); - } - return; - } - skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode)); - chan = &mode->channels[wpa_s->mlme.scan_channel_idx]; - if ((chan->flag & HOSTAPD_CHAN_DISABLED) || - (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) || - (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) && - mode->mode == HOSTAPD_MODE_IEEE80211B && - wpa_s->mlme.scan_skip_11b)) - skip = 1; - if (!skip && wpa_s->mlme.scan_freqs) { - int i, found = 0; - for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) { - if (wpa_s->mlme.scan_freqs[i] == chan->freq) { - found = 1; - break; - } - } - if (!found) - skip = 1; - } - - if (!skip) { - wpa_printf(MSG_MSGDUMP, - "MLME: scan channel %d (%d MHz)", - chan->chan, chan->freq); - - wpa_s->mlme.channel = chan->chan; - wpa_s->mlme.freq = chan->freq; - wpa_s->mlme.phymode = mode->mode; - if (ieee80211_sta_set_channel(wpa_s, mode->mode, - chan->chan, chan->freq)) - { - wpa_printf(MSG_DEBUG, "MLME: failed to set " - "channel %d (%d MHz) for scan", - chan->chan, chan->freq); - skip = 1; - } - } - - wpa_s->mlme.scan_channel_idx++; - if (wpa_s->mlme.scan_channel_idx >= - wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx]. - num_channels) { - wpa_s->mlme.scan_hw_mode_idx++; - wpa_s->mlme.scan_channel_idx = 0; - } - - if (skip) { - timeout = 0; - break; - } - - timeout = IEEE80211_PROBE_DELAY; - wpa_s->mlme.scan_state = SCAN_SEND_PROBE; - break; - case SCAN_SEND_PROBE: - if (ieee80211_active_scan(wpa_s)) { - ieee80211_send_probe_req(wpa_s, NULL, - wpa_s->mlme.scan_ssid, - wpa_s->mlme.scan_ssid_len); - timeout = IEEE80211_CHANNEL_TIME; - } else { - timeout = IEEE80211_PASSIVE_CHANNEL_TIME; - } - wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; - break; - } - - eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000), - ieee80211_sta_scan_timer, wpa_s, NULL); -} - - -int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params) -{ - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (ssid_len > MAX_SSID_LEN) - return -1; - - /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) - * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS - * BSSID: MACAddress - * SSID - * ScanType: ACTIVE, PASSIVE - * ProbeDelay: delay (in microseconds) to be used prior to transmitting - * a Probe frame during active scanning - * ChannelList - * MinChannelTime (>= ProbeDelay), in TU - * MaxChannelTime: (>= MinChannelTime), in TU - */ - - /* MLME-SCAN.confirm - * BSSDescriptionSet - * ResultCode: SUCCESS, INVALID_PARAMETERS - */ - - /* TODO: if assoc, move to power save mode for the duration of the - * scan */ - - if (wpa_s->mlme.sta_scanning) - return -1; - - wpa_printf(MSG_DEBUG, "MLME: starting scan"); - - ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies, - params->extra_ies_len); - - os_free(wpa_s->mlme.scan_freqs); - if (params->freqs) { - int i; - for (i = 0; params->freqs[i]; i++) - ; - wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int)); - if (wpa_s->mlme.scan_freqs) - os_memcpy(wpa_s->mlme.scan_freqs, params->freqs, - (i + 1) * sizeof(int)); - } else - wpa_s->mlme.scan_freqs = NULL; - - ieee80211_sta_save_oper_chan(wpa_s); - - wpa_s->mlme.sta_scanning = 1; - /* TODO: stop TX queue? */ - - if (ssid) { - wpa_s->mlme.scan_ssid_len = ssid_len; - os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len); - } else - wpa_s->mlme.scan_ssid_len = 0; - wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not - * supported */ - wpa_s->mlme.scan_state = SCAN_SET_CHANNEL; - wpa_s->mlme.scan_hw_mode_idx = 0; - wpa_s->mlme.scan_channel_idx = 0; - eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL); - - return 0; -} - - -struct wpa_scan_results * -ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) -{ - size_t ap_num = 0; - struct wpa_scan_results *res; - struct wpa_scan_res *r; - struct ieee80211_sta_bss *bss; - - res = os_zalloc(sizeof(*res)); - for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) - ap_num++; - res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - return NULL; - } - - for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) { - r = os_zalloc(sizeof(*r) + bss->ie_len); - if (r == NULL) - break; - os_memcpy(r->bssid, bss->bssid, ETH_ALEN); - r->freq = bss->freq; - r->beacon_int = bss->beacon_int; - r->caps = bss->capability; - r->level = bss->rssi; - r->tsf = bss->timestamp; - if (bss->ie) { - r->ie_len = bss->ie_len; - os_memcpy(r + 1, bss->ie, bss->ie_len); - } - - res->res[res->num++] = r; - } - - return res; -} - - -#if 0 /* FIX */ -struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s, - struct sk_buff *skb, u8 *bssid, - u8 *addr) -{ - struct ieee80211_local *local = dev->priv; - struct list_head *ptr; - struct sta_info *sta; - struct wpa_supplicant *sta_dev = NULL; - - /* TODO: Could consider removing the least recently used entry and - * allow new one to be added. */ - if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { - if (net_ratelimit()) { - wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS " - "STA entry " MACSTR, MAC2STR(addr)); - } - return NULL; - } - - spin_lock_bh(&local->sub_if_lock); - list_for_each(ptr, &local->sub_if_list) { - sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); - if (sdata->type == IEEE80211_SUB_IF_TYPE_STA && - os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { - sta_dev = sdata->dev; - break; - } - } - spin_unlock_bh(&local->sub_if_lock); - - if (sta_dev == NULL) - return NULL; - - wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR - " (dev=%s)", MAC2STR(addr), sta_dev->name); - - sta = sta_info_add(wpa_s, addr); - if (sta == NULL) { - return NULL; - } - - sta->dev = sta_dev; - sta->supp_rates = wpa_s->mlme.supp_rates_bits; - - rate_control_rate_init(local, sta); - - return sta; /* caller will call sta_info_release() */ -} -#endif - - -int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason) -{ - wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason); - - ieee80211_send_deauth(wpa_s, reason); - ieee80211_set_associated(wpa_s, 0); - return 0; -} - - -int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason) -{ - wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason); - - if (!wpa_s->mlme.associated) - return -1; - - ieee80211_send_disassoc(wpa_s, reason); - ieee80211_set_associated(wpa_s, 0); - return 0; -} - - -void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status) -{ - struct ieee80211_mgmt *mgmt; - u16 fc; - const u8 *pos; - - /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */ - - if (wpa_s->mlme.sta_scanning) { - ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status); - return; - } - - if (len < 24) - return; - - mgmt = (struct ieee80211_mgmt *) buf; - fc = le_to_host16(mgmt->frame_control); - - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) - ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status); - else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { - if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != - WLAN_FC_FROMDS) - return; - /* mgmt->sa is actually BSSID for FromDS data frames */ - if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) - return; - /* Skip IEEE 802.11 and LLC headers */ - pos = buf + 24 + 6; - if (WPA_GET_BE16(pos) != ETH_P_EAPOL) - return; - pos += 2; - /* mgmt->bssid is actually BSSID for SA data frames */ - wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid, - pos, buf + len - pos); - } -} - - -void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features) -{ - size_t i; - - if (hw_features == NULL) - return; - - for (i = 0; i < num_hw_features; i++) { - os_free(hw_features[i].channels); - os_free(hw_features[i].rates); - } - - os_free(hw_features); -} - - -int ieee80211_sta_init(struct wpa_supplicant *wpa_s) -{ - u16 num_modes, flags; - - wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, - &flags); - if (wpa_s->mlme.modes == NULL) { - wpa_printf(MSG_ERROR, "MLME: Failed to read supported " - "channels and rates from the driver"); - return -1; - } - - wpa_s->mlme.num_modes = num_modes; - - wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A; - wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B; - wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G; - - wpa_s->mlme.wmm_enabled = 1; - - return 0; -} - - -void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) -{ - eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL); - eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL); - os_free(wpa_s->mlme.extra_ie); - wpa_s->mlme.extra_ie = NULL; - os_free(wpa_s->mlme.extra_probe_ie); - wpa_s->mlme.extra_probe_ie = NULL; - os_free(wpa_s->mlme.assocreq_ies); - wpa_s->mlme.assocreq_ies = NULL; - os_free(wpa_s->mlme.assocresp_ies); - wpa_s->mlme.assocresp_ies = NULL; - ieee80211_bss_list_deinit(wpa_s); - ieee80211_sta_free_hw_features(wpa_s->mlme.modes, - wpa_s->mlme.num_modes); -#ifdef CONFIG_IEEE80211R - os_free(wpa_s->mlme.ft_ies); - wpa_s->mlme.ft_ies = NULL; - wpa_s->mlme.ft_ies_len = 0; -#endif /* CONFIG_IEEE80211R */ - - os_free(wpa_s->mlme.scan_freqs); - wpa_s->mlme.scan_freqs = NULL; -} - - -#ifdef CONFIG_IEEE80211R - -int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, - const u8 *ies, size_t ies_len) -{ - if (md == NULL) { - wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain"); - os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN); - } else { - wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR, - MAC2STR(md)); - os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN); - } - - wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len); - os_free(wpa_s->mlme.ft_ies); - wpa_s->mlme.ft_ies = os_malloc(ies_len); - if (wpa_s->mlme.ft_ies == NULL) - return -1; - os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len); - wpa_s->mlme.ft_ies_len = ies_len; - - return 0; -} - - -int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, - const u8 *target_ap, - const u8 *ies, size_t ies_len) -{ - u8 *buf; - size_t len; - struct ieee80211_mgmt *mgmt; - int res; - - /* - * Action frame payload: - * Category[1] = 6 (Fast BSS Transition) - * Action[1] = 1 (Fast BSS Transition Request) - * STA Address - * Target AP Address - * FT IEs - */ - - buf = os_zalloc(sizeof(*mgmt) + ies_len); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " - "FT action frame"); - return -1; - } - - mgmt = (struct ieee80211_mgmt *) buf; - len = 24; - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_FT; - mgmt->u.action.u.ft_action_req.action = action; - os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr, - ETH_ALEN); - os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap, - ETH_ALEN); - os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len); - len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len; - - wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d " - "Target AP=" MACSTR " body_len=%lu", - action, MAC2STR(target_ap), (unsigned long) ies_len); - - res = ieee80211_sta_tx(wpa_s, buf, len); - os_free(buf); - - return res; -} - -#endif /* CONFIG_IEEE80211R */ - - -static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s, - const u8 *ies, size_t ies_len) -{ - os_free(wpa_s->mlme.extra_probe_ie); - wpa_s->mlme.extra_probe_ie = NULL; - wpa_s->mlme.extra_probe_ie_len = 0; - - if (ies == NULL) - return 0; - - wpa_s->mlme.extra_probe_ie = os_malloc(ies_len); - if (wpa_s->mlme.extra_probe_ie == NULL) - return -1; - - os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len); - wpa_s->mlme.extra_probe_ie_len = ies_len; - - return 0; -} diff --git a/wpa_supplicant/mlme.h b/wpa_supplicant/mlme.h deleted file mode 100644 index 5db3665..0000000 --- a/wpa_supplicant/mlme.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * WPA Supplicant - Client mode MLME - * Copyright (c) 2003-2007, Jouni Malinen - * Copyright (c) 2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * - * 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. - */ - -#ifndef MLME_H -#define MLME_H - -struct wpa_supplicant; - -struct ieee80211_rx_status { - int freq; - int channel; - int ssi; -}; - -#ifdef CONFIG_CLIENT_MLME - -int ieee80211_sta_init(struct wpa_supplicant *wpa_s); -void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s); -int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params); -int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason); -int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason); -int ieee80211_sta_associate(struct wpa_supplicant *wpa_s, - struct wpa_driver_associate_params *params); -int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid, - size_t *len); -void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features); -void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status); -struct wpa_scan_results * -ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s); -int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, - const u8 *ies, size_t ies_len); -int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, - const u8 *target_ap, - const u8 *ies, size_t ies_len); - -#else /* CONFIG_CLIENT_MLME */ - -static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s) -{ - return 0; -} - -static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s) -{ -} - -static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params) -{ - return -1; -} - -static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, - u16 reason) -{ - return -1; -} - -static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, - u16 reason) -{ - return -1; -} - -static inline int -ieee80211_sta_associate(struct wpa_supplicant *wpa_s, - struct wpa_driver_associate_params *params) -{ - return -1; -} - -static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, - u8 *ssid, size_t *len) -{ - return -1; -} - -static inline void -ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features) -{ -} - -static inline void -ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status) -{ -} - -static inline struct wpa_scan_results * -ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s) -{ - return NULL; -} - -static inline int -ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, - const u8 *ies, size_t ies_len) -{ - return -1; -} - -static inline int -ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action, - const u8 *target_ap, - const u8 *ies, size_t ies_len) -{ - return -1; -} - -#endif /* CONFIG_CLIENT_MLME */ - -#endif /* MLME_H */ diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index ac65b4f..136d25f 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -22,8 +22,11 @@ #include "dbus/dbus_common.h" #include "dbus/dbus_old.h" #include "dbus/dbus_new.h" +#include "rsn_supp/wpa.h" #include "driver_i.h" #include "scan.h" +#include "p2p_supplicant.h" +#include "sme.h" #include "notify.h" int wpas_notify_supplicant_initialized(struct wpa_global *global) @@ -81,6 +84,22 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); + +#ifdef CONFIG_P2P + if (new_state == WPA_COMPLETED) + wpas_p2p_notif_connected(wpa_s); + else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) + wpas_p2p_notif_disconnected(wpa_s); +#endif /* CONFIG_P2P */ + + sme_state_changed(wpa_s); + +#ifdef ANDROID + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE + "id=%d state=%d BSSID=" MACSTR, + wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, + new_state, MAC2STR(wpa_s->pending_bssid)); +#endif /* ANDROID */ } @@ -102,6 +121,12 @@ void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s) } +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE); +} + + void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -116,6 +141,15 @@ void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, } +void wpas_notify_network_request(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, + const char *default_txt) +{ + wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt); +} + + void wpas_notify_scanning(struct wpa_supplicant *wpa_s) { /* notify the old DBus API */ @@ -182,14 +216,45 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - wpas_dbus_register_network(wpa_s, ssid); + /* + * Networks objects created during any P2P activities should not be + * exposed out. They might/will confuse certain non-P2P aware + * applications since these network objects won't behave like + * regular ones. + */ + if (wpa_s->global->p2p_group_formation != wpa_s) + wpas_dbus_register_network(wpa_s, ssid); +} + + +void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +#ifdef CONFIG_P2P + wpas_dbus_register_persistent_group(wpa_s, ssid); +#endif /* CONFIG_P2P */ +} + + +void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +#ifdef CONFIG_P2P + wpas_dbus_unregister_persistent_group(wpa_s, ssid->id); +#endif /* CONFIG_P2P */ } void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - wpas_dbus_unregister_network(wpa_s, ssid->id); + if (wpa_s->wpa) + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + if (wpa_s->global->p2p_group_formation != wpa_s) + wpas_dbus_unregister_network(wpa_s, ssid->id); +#ifdef CONFIG_P2P + wpas_p2p_network_removed(wpa_s, ssid); +#endif /* CONFIG_P2P */ } @@ -337,3 +402,209 @@ void wpas_notify_resume(struct wpa_global *global) wpa_supplicant_req_scan(wpa_s, 0, 100000); } } + + +#ifdef CONFIG_P2P + +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int new_device) +{ + if (new_device) { + /* Create the new peer object */ + wpas_dbus_register_peer(wpa_s, dev_addr); + } + + /* Notify a new peer has been detected*/ + wpas_dbus_signal_peer_device_found(wpa_s, dev_addr); +} + + +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + wpas_dbus_unregister_peer(wpa_s, dev_addr); + + /* Create signal on interface object*/ + wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr); +} + + +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + const char *role) +{ + wpas_dbus_unregister_p2p_group(wpa_s, ssid); + + wpas_dbus_signal_p2p_group_removed(wpa_s, role); +} + + +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id) +{ + wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id); +} + + +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res) +{ + wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res); +} + + +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid) +{ + wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid); +} + + +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len) +{ + wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); +} + + +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic, + tlvs, tlvs_len); +} + + +/** + * wpas_notify_p2p_provision_discovery - Notification of provision discovery + * @dev_addr: Who sent the request or responded to our request. + * @request: Will be 1 if request, 0 for response. + * @status: Valid only in case of response (0 in case of success) + * @config_methods: WPS config methods + * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method + * + * This can be used to notify: + * - Requests or responses + * - Various config methods + * - Failure condition in case of response + */ +void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin) +{ + wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request, + status, config_methods, + generated_pin); +} + + +void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int network_id, + int client) +{ + /* Notify a group has been started */ + wpas_dbus_register_p2p_group(wpa_s, ssid); + + wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id); +} + + +void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + wpas_dbus_signal_p2p_wps_failed(wpa_s, fail); +} + +#endif /* CONFIG_P2P */ + + +static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta, + const u8 *p2p_dev_addr) +{ +#ifdef CONFIG_P2P + wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr); + + /* + * Register a group member object corresponding to this peer and + * emit a PeerJoined signal. This will check if it really is a + * P2P group. + */ + wpas_dbus_register_p2p_groupmember(wpa_s, sta); + + /* + * Create 'peer-joined' signal on group object -- will also + * check P2P itself. + */ + wpas_dbus_signal_p2p_peer_joined(wpa_s, sta); +#endif /* CONFIG_P2P */ +} + + +static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +#ifdef CONFIG_P2P + /* + * Unregister a group member object corresponding to this peer + * if this is a P2P group. + */ + wpas_dbus_unregister_p2p_groupmember(wpa_s, sta); + + /* + * Create 'peer-disconnected' signal on group object if this + * is a P2P group. + */ + wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta); +#endif /* CONFIG_P2P */ +} + + +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *mac_addr, int authorized, + const u8 *p2p_dev_addr) +{ + if (authorized) + wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr); + else + wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr); +} + + +void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, + const char *subject, const char *cert_hash, + const struct wpabuf *cert) +{ + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s'%s%s", + depth, subject, + cert_hash ? " hash=" : "", + cert_hash ? cert_hash : ""); + + if (cert) { + char *cert_hex; + size_t len = wpabuf_len(cert) * 2 + 1; + cert_hex = os_malloc(len); + if (cert_hex) { + wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert), + wpabuf_len(cert)); + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s' cert=%s", + depth, subject, cert_hex); + os_free(cert_hex); + } + } + + /* notify the old DBus API */ + wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject, + cert_hash, cert); + /* notify the new DBus API */ + wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert); +} diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 2e70bdb..236a31e 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -15,6 +15,8 @@ #ifndef NOTIFY_H #define NOTIFY_H +#include "p2p/p2p.h" + struct wps_credential; struct wps_event_m2d; struct wps_event_fail; @@ -29,10 +31,15 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s); void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void wpas_notify_network_request(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, + const char *default_txt); void wpas_notify_scanning(struct wpa_supplicant *wpa_s); void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success); void wpas_notify_scan_results(struct wpa_supplicant *wpa_s); @@ -78,4 +85,47 @@ void wpas_notify_debug_show_keys_changed(struct wpa_global *global); void wpas_notify_suspend(struct wpa_global *global); void wpas_notify_resume(struct wpa_global *global); +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *mac_addr, int authorized, + const u8 *p2p_dev_addr); +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int new_device); +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s, + const u8 *dev_addr); +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + const char *role); +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id); +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res); +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *bssid); +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s, + int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, + size_t tlvs_len); +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); +void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin); +void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int network_id, + int client); +void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + +void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); + +void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, + const char *subject, const char *cert_hash, + const struct wpabuf *cert); + #endif /* NOTIFY_H */ diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c new file mode 100644 index 0000000..790f14a --- /dev/null +++ b/wpa_supplicant/offchannel.c @@ -0,0 +1,314 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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 "utils/eloop.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "offchannel.h" + + + +static struct wpa_supplicant * +wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) +{ + struct wpa_supplicant *iface; + + if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) + return wpa_s; + + /* + * Try to find a group interface that matches with the source address. + */ + iface = wpa_s->global->ifaces; + while (iface) { + if (os_memcmp(wpa_s->pending_action_src, + iface->own_addr, ETH_ALEN) == 0) + break; + iface = iface->next; + } + if (iface) { + wpa_printf(MSG_DEBUG, "P2P: Use group interface %s " + "instead of interface %s for Action TX", + iface->ifname, wpa_s->ifname); + return iface; + } + + return wpa_s; +} + + +static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_supplicant *iface; + int res; + int without_roc; + + without_roc = wpa_s->pending_action_without_roc; + wpa_s->pending_action_without_roc = 0; + wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback " + "(without_roc=%d pending_action_tx=%p)", + without_roc, wpa_s->pending_action_tx); + + if (wpa_s->pending_action_tx == NULL) + return; + + /* + * This call is likely going to be on the P2P device instance if the + * driver uses a separate interface for that purpose. However, some + * Action frames are actually sent within a P2P Group and when that is + * the case, we need to follow power saving (e.g., GO buffering the + * frame for a client in PS mode or a client following the advertised + * NoA from its GO). To make that easier for the driver, select the + * correct group interface here. + */ + iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src); + + if (wpa_s->off_channel_freq != wpa_s->pending_action_freq && + wpa_s->pending_action_freq != 0 && + wpa_s->pending_action_freq != iface->assoc_freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX " + "waiting for another freq=%u (off_channel_freq=%u " + "assoc_freq=%u)", + wpa_s->pending_action_freq, + wpa_s->off_channel_freq, + iface->assoc_freq); + if (without_roc && wpa_s->off_channel_freq == 0) { + /* + * We may get here if wpas_send_action() found us to be + * on the correct channel, but remain-on-channel cancel + * event was received before getting here. + */ + wpa_printf(MSG_DEBUG, "Off-channel: Schedule " + "remain-on-channel to send Action frame"); + if (wpa_drv_remain_on_channel( + wpa_s, wpa_s->pending_action_freq, 200) < + 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to " + "request driver to remain on " + "channel (%u MHz) for Action Frame " + "TX", wpa_s->pending_action_freq); + } else { + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = + wpa_s->pending_action_freq; + } + } + return; + } + + wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to " + MACSTR " using interface %s", + MAC2STR(wpa_s->pending_action_dst), iface->ifname); + res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, + wpa_s->pending_action_dst, + wpa_s->pending_action_src, + wpa_s->pending_action_bssid, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx), + wpa_s->pending_action_no_cck); + if (res) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the " + "pending Action frame"); + /* + * Use fake TX status event to allow state machines to + * continue. + */ + offchannel_send_action_tx_status( + wpa_s, wpa_s->pending_action_dst, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx), + OFFCHANNEL_SEND_ACTION_FAILED); + } +} + + +void offchannel_send_action_tx_status( + struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, + size_t data_len, enum offchannel_send_action_result result) +{ + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "no pending operation"); + return; + } + + if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "unknown destination address"); + return; + } + + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + + wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p", + result, wpa_s->pending_action_tx_status_cb); + + if (wpa_s->pending_action_tx_status_cb) { + wpa_s->pending_action_tx_status_cb( + wpa_s, wpa_s->pending_action_freq, + wpa_s->pending_action_dst, wpa_s->pending_action_src, + wpa_s->pending_action_bssid, + data, data_len, result); + } +} + + +int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *buf, size_t len, unsigned int wait_time, + void (*tx_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result), + int no_cck) +{ + wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst=" + MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d", + freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + + wpa_s->pending_action_tx_status_cb = tx_cb; + + if (wpa_s->pending_action_tx) { + wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action " + "frame TX to " MACSTR, + MAC2STR(wpa_s->pending_action_dst)); + wpabuf_free(wpa_s->pending_action_tx); + } + wpa_s->pending_action_tx = wpabuf_alloc(len); + if (wpa_s->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action " + "frame TX buffer (len=%llu)", + (unsigned long long) len); + return -1; + } + wpabuf_put_data(wpa_s->pending_action_tx, buf, len); + os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN); + os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN); + os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); + wpa_s->pending_action_freq = freq; + wpa_s->pending_action_no_cck = no_cck; + + if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + struct wpa_supplicant *iface; + + iface = wpas_get_tx_interface(wpa_s, + wpa_s->pending_action_src); + wpa_s->action_tx_wait_time = wait_time; + + return wpa_drv_send_action( + iface, wpa_s->pending_action_freq, + wait_time, wpa_s->pending_action_dst, + wpa_s->pending_action_src, wpa_s->pending_action_bssid, + wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx), + wpa_s->pending_action_no_cck); + } + + if (freq) { + struct wpa_supplicant *tx_iface; + tx_iface = wpas_get_tx_interface(wpa_s, src); + if (tx_iface->assoc_freq == freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Already on " + "requested channel (TX interface operating " + "channel)"); + freq = 0; + } + } + + if (wpa_s->off_channel_freq == freq || freq == 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Already on requested " + "channel; send Action frame immediately"); + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + wpa_s->pending_action_without_roc = 1; + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); + eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL); + return 0; + } + wpa_s->pending_action_without_roc = 0; + + if (wpa_s->roc_waiting_drv_freq == freq) { + wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for " + "driver to get to frequency %u MHz; continue " + "waiting to send the Action frame", freq); + return 0; + } + + wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be " + "transmitted once the driver gets to the requested " + "channel"); + if (wait_time > wpa_s->max_remain_on_chan) + wait_time = wpa_s->max_remain_on_chan; + if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " + "to remain on channel (%u MHz) for Action " + "Frame TX", freq); + return -1; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = freq; + + return 0; +} + + +void offchannel_send_action_done(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done " + "notification"); + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX && + wpa_s->action_tx_wait_time) + wpa_drv_send_action_cancel_wait(wpa_s); + + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } +} + + +void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + wpa_s->roc_waiting_drv_freq = 0; + wpa_s->off_channel_freq = freq; + wpas_send_action_cb(wpa_s, NULL); +} + + +void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_s->off_channel_freq = 0; +} + + +void offchannel_deinit(struct wpa_supplicant *wpa_s) +{ + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); +} diff --git a/wpa_supplicant/offchannel.h b/wpa_supplicant/offchannel.h new file mode 100644 index 0000000..60e0d03 --- /dev/null +++ b/wpa_supplicant/offchannel.h @@ -0,0 +1,39 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * 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. + */ + +#ifndef OFFCHANNEL_H +#define OFFCHANNEL_H + +int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *buf, size_t len, unsigned int wait_time, + void (*tx_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result), + int no_cck); +void offchannel_send_action_done(struct wpa_supplicant *wpa_s); +void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration); +void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void offchannel_deinit(struct wpa_supplicant *wpa_s); +void offchannel_send_action_tx_status( + struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, + size_t data_len, enum offchannel_send_action_result result); + +#endif /* OFFCHANNEL_H */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c new file mode 100644 index 0000000..ed0ad59 --- /dev/null +++ b/wpa_supplicant/p2p_supplicant.c @@ -0,0 +1,4474 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "wps/wps_i.h" +#include "p2p/p2p.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/p2p_hostapd.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "ap.h" +#include "config_ssid.h" +#include "config.h" +#include "notify.h" +#include "scan.h" +#include "bss.h" +#include "offchannel.h" +#include "wps_supplicant.h" +#include "p2p_supplicant.h" + + +/* + * How many times to try to scan to find the GO before giving up on join + * request. + */ +#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10 + +#ifndef P2P_MAX_CLIENT_IDLE +/* + * How many seconds to try to reconnect to the GO when connection in P2P client + * role has been lost. + */ +#define P2P_MAX_CLIENT_IDLE 10 +#endif /* P2P_MAX_CLIENT_IDLE */ + + +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx); +static struct wpa_supplicant * +wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, + int go); +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s); +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); +static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, + const u8 *dev_addr, enum p2p_wps_method wps_method); +static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, + void *timeout_ctx); +static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s); +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s); +static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s); + + +static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)", + (int) scan_res->num); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid, + bss->freq, bss->level, + (const u8 *) (bss + 1), + bss->ie_len) > 0) + break; + } + + p2p_scan_res_handled(wpa_s->global->p2p); +} + + +static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + size_t ielen; + int was_in_p2p_scan; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + + /* P2P Wildcard SSID */ + params.num_ssids = 1; + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, + WPS_REQ_ENROLLEE, + num_req_dev_types, req_dev_types); + if (wps_ie == NULL) + return -1; + + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + return -1; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(wpa_s->global->p2p, ies, dev_id); + + params.p2p_probe = 1; + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + switch (type) { + case P2P_SCAN_SOCIAL: + params.freqs = social_channels; + break; + case P2P_SCAN_FULL: + break; + case P2P_SCAN_SPECIFIC: + social_channels[0] = freq; + social_channels[1] = 0; + params.freqs = social_channels; + break; + case P2P_SCAN_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + was_in_p2p_scan = wpa_s->scan_res_handler == wpas_p2p_scan_res_handler; + wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + ret = wpa_drv_scan(wpa_s, ¶ms); + + wpabuf_free(ies); + + if (ret) { + wpa_s->scan_res_handler = NULL; + if (wpa_s->scanning || was_in_p2p_scan) { + wpa_s->p2p_cb_on_scan_complete = 1; + ret = 1; + } + } + + return ret; +} + + +static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface) +{ + switch (p2p_group_interface) { + case P2P_GROUP_INTERFACE_PENDING: + return WPA_IF_P2P_GROUP; + case P2P_GROUP_INTERFACE_GO: + return WPA_IF_P2P_GO; + case P2P_GROUP_INTERFACE_CLIENT: + return WPA_IF_P2P_CLIENT; + } + + return WPA_IF_P2P_GROUP; +} + + +static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, + const u8 *ssid, + size_t ssid_len, int *go) +{ + struct wpa_ssid *s; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled != 0 || !s->p2p_group || + s->ssid_len != ssid_len || + os_memcmp(ssid, s->ssid, ssid_len) != 0) + continue; + if (s->mode == WPAS_MODE_P2P_GO && + s != wpa_s->current_ssid) + continue; + if (go) + *go = s->mode == WPAS_MODE_P2P_GO; + return wpa_s; + } + } + + return NULL; +} + + +static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + char *gtype; + const char *reason; + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + /* + * The current SSID was not known, but there may still be a + * pending P2P group interface waiting for provisioning. + */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->p2p_group && + (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || + (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) + break; + ssid = ssid->next; + } + } + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) + gtype = "GO"; + else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || + (ssid && ssid->mode == WPAS_MODE_INFRA)) { + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + gtype = "client"; + } else + gtype = "GO"; + if (wpa_s->cross_connect_in_use) { + wpa_s->cross_connect_in_use = 0; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + } + switch (wpa_s->removal_reason) { + case P2P_GROUP_REMOVAL_REQUESTED: + reason = " reason=REQUESTED"; + break; + case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: + reason = " reason=IDLE"; + break; + case P2P_GROUP_REMOVAL_UNAVAILABLE: + reason = " reason=UNAVAILABLE"; + break; + default: + reason = ""; + break; + } + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", + wpa_s->ifname, gtype, reason); + + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + + if (ssid) + wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); + + if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + struct wpa_global *global; + char *ifname; + enum wpa_driver_if_type type; + wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s", + wpa_s->ifname); + global = wpa_s->global; + ifname = os_strdup(wpa_s->ifname); + type = wpas_p2p_if_type(wpa_s->p2p_group_interface); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s); + wpa_s = global->ifaces; + if (wpa_s && ifname) + wpa_drv_if_remove(wpa_s, type, ifname); + os_free(ifname); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); + if (ssid && (ssid->p2p_group || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || + (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) { + int id = ssid->id; + if (ssid == wpa_s->current_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->current_ssid = NULL; + } + /* + * Networks objects created during any P2P activities are not + * exposed out as they might/will confuse certain non-P2P aware + * applications since these network objects won't behave like + * regular ones. + * + * Likewise, we don't send out network removed signals for such + * network objects. + */ + wpa_config_remove_network(wpa_s->conf, id); + wpa_supplicant_clear_status(wpa_s); + wpa_supplicant_cancel_sched_scan(wpa_s); + } else { + wpa_printf(MSG_DEBUG, "P2P: Temporary group network not " + "found"); + } + if (wpa_s->ap_iface) + wpa_supplicant_ap_deinit(wpa_s); + else + wpa_drv_deinit_p2p_cli(wpa_s); +} + + +static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, + u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; + const u8 *bssid; + struct wpabuf *p2p; + u8 group_capab; + const u8 *addr; + + if (wpa_s->go_params) + bssid = wpa_s->go_params->peer_interface_addr; + else + bssid = wpa_s->bssid; + + bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); + if (bss == NULL) { + u8 iface_addr[ETH_ALEN]; + if (p2p_get_interface_addr(wpa_s->global->p2p, bssid, + iface_addr) == 0) + bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len); + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " + "group is persistent - BSS " MACSTR " not found", + MAC2STR(bssid)); + return 0; + } + + p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " + "group is persistent - BSS " MACSTR + " did not include P2P IE", MAC2STR(bssid)); + wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs", + (u8 *) (bss + 1), bss->ie_len); + wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs", + ((u8 *) bss + 1) + bss->ie_len, + bss->beacon_ie_len); + return 0; + } + + group_capab = p2p_get_group_capab(p2p); + addr = p2p_get_go_dev_addr(p2p); + wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: " + "group_capab=0x%x", group_capab); + if (addr) { + os_memcpy(go_dev_addr, addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR, + MAC2STR(addr)); + } else + os_memset(go_dev_addr, 0, ETH_ALEN); + wpabuf_free(p2p); + + wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x " + "go_dev_addr=" MACSTR, + MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr)); + + return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP; +} + + +static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *go_dev_addr) +{ + struct wpa_ssid *s; + int changed = 0; + + wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent " + "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr)); + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 && + s->ssid_len == ssid->ssid_len && + os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0) + break; + } + + if (s) { + wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group " + "entry"); + if (ssid->passphrase && !s->passphrase) + changed = 1; + else if (ssid->passphrase && s->passphrase && + os_strcmp(ssid->passphrase, s->passphrase) != 0) + changed = 1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group " + "entry"); + changed = 1; + s = wpa_config_add_network(wpa_s->conf); + if (s == NULL) + return -1; + + /* + * Instead of network_added we emit persistent_group_added + * notification. Also to keep the defense checks in + * persistent_group obj registration method, we set the + * relevant flags in s to designate it as a persistent group. + */ + s->p2p_group = 1; + s->p2p_persistent_group = 1; + wpas_notify_persistent_group_added(wpa_s, s); + wpa_config_set_network_defaults(s); + } + + s->p2p_group = 1; + s->p2p_persistent_group = 1; + s->disabled = 2; + s->bssid_set = 1; + os_memcpy(s->bssid, go_dev_addr, ETH_ALEN); + s->mode = ssid->mode; + s->auth_alg = WPA_AUTH_ALG_OPEN; + s->key_mgmt = WPA_KEY_MGMT_PSK; + s->proto = WPA_PROTO_RSN; + s->pairwise_cipher = WPA_CIPHER_CCMP; + s->export_keys = 1; + if (ssid->passphrase) { + os_free(s->passphrase); + s->passphrase = os_strdup(ssid->passphrase); + } + if (ssid->psk_set) { + s->psk_set = 1; + os_memcpy(s->psk, ssid->psk, 32); + } + if (s->passphrase && !s->psk_set) + wpa_config_update_psk(s); + if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) { + os_free(s->ssid); + s->ssid = os_malloc(ssid->ssid_len); + } + if (s->ssid) { + s->ssid_len = ssid->ssid_len; + os_memcpy(s->ssid, ssid->ssid, s->ssid_len); + } + +#ifndef CONFIG_NO_CONFIG_WRITE + if (changed && wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + + return s->id; +} + + +static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + struct wpa_ssid *ssid, *s; + u8 *n; + size_t i; + + ssid = wpa_s->current_ssid; + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + !ssid->p2p_persistent_group) + return; + + for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) + continue; + + if (s->ssid_len == ssid->ssid_len && + os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0) + break; + } + + if (s == NULL) + return; + + for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) { + if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr, + ETH_ALEN) == 0) + return; /* already in list */ + } + + n = os_realloc(s->p2p_client_list, + (s->num_p2p_clients + 1) * ETH_ALEN); + if (n == NULL) + return; + os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); + s->p2p_client_list = n; + s->num_p2p_clients++; + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + +static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, + int success) +{ + struct wpa_ssid *ssid; + const char *ssid_txt; + int client; + int persistent; + u8 go_dev_addr[ETH_ALEN]; + int network_id = -1; + + /* + * This callback is likely called for the main interface. Update wpa_s + * to use the group interface if a new interface was created for the + * group. + */ + if (wpa_s->global->p2p_group_formation) + wpa_s = wpa_s->global->p2p_group_formation; + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + + if (!success) { + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_p2p_group_delete(wpa_s); + return; + } + + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); + + ssid = wpa_s->current_ssid; + if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + ssid->mode = WPAS_MODE_P2P_GO; + p2p_group_notif_formation_done(wpa_s->p2p_group); + wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL); + } + + persistent = 0; + if (ssid) { + ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len); + client = ssid->mode == WPAS_MODE_INFRA; + if (ssid->mode == WPAS_MODE_P2P_GO) { + persistent = ssid->p2p_persistent_group; + os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr, + ETH_ALEN); + } else + persistent = wpas_p2p_persistent_group(wpa_s, + go_dev_addr, + ssid->ssid, + ssid->ssid_len); + } else { + ssid_txt = ""; + client = wpa_s->p2p_group_interface == + P2P_GROUP_INTERFACE_CLIENT; + os_memset(go_dev_addr, 0, ETH_ALEN); + } + + wpa_s->show_group_started = 0; + if (client) { + /* + * Indicate event only after successfully completed 4-way + * handshake, i.e., when the interface is ready for data + * packets. + */ + wpa_s->show_group_started = 1; + } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" MACSTR + "%s", + wpa_s->ifname, ssid_txt, ssid->frequency, psk, + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + wpas_p2p_cross_connect_setup(wpa_s); + wpas_p2p_set_group_idle_timeout(wpa_s); + } else { + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " + "go_dev_addr=" MACSTR "%s", + wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0, + ssid && ssid->passphrase ? ssid->passphrase : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + wpas_p2p_cross_connect_setup(wpa_s); + wpas_p2p_set_group_idle_timeout(wpa_s); + } + + if (persistent) + network_id = wpas_p2p_store_persistent_group(wpa_s->parent, + ssid, go_dev_addr); + if (network_id < 0 && ssid) + network_id = ssid->id; + if (!client) + wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); +} + + +static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result) +{ + enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS; + + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + switch (result) { + case OFFCHANNEL_SEND_ACTION_SUCCESS: + res = P2P_SEND_ACTION_SUCCESS; + break; + case OFFCHANNEL_SEND_ACTION_NO_ACK: + res = P2P_SEND_ACTION_NO_ACK; + break; + case OFFCHANNEL_SEND_ACTION_FAILED: + res = P2P_SEND_ACTION_FAILED; + break; + } + + p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res); + + if (result != OFFCHANNEL_SEND_ACTION_SUCCESS && + wpa_s->pending_pd_before_join && + (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || + os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation (no ACK for PD " + "Req)"); + wpas_p2p_join_start(wpa_s); + } +} + + +static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + struct wpa_supplicant *wpa_s = ctx; + return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len, + wait_time, + wpas_p2p_send_action_tx_status, 1); +} + + +static void wpas_send_action_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + offchannel_send_action_done(wpa_s); +} + + +static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params) +{ + if (wpa_s->go_params == NULL) { + wpa_s->go_params = os_malloc(sizeof(*params)); + if (wpa_s->go_params == NULL) + return -1; + } + os_memcpy(wpa_s->go_params, params, sizeof(*params)); + return 0; +} + + +static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res) +{ + wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR, + MAC2STR(res->peer_interface_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID", + res->ssid, res->ssid_len); + wpa_supplicant_ap_deinit(wpa_s); + wpas_copy_go_neg_results(wpa_s, res); + if (res->wps_method == WPS_PBC) + wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); + else { + u16 dev_pw_id = DEV_PW_DEFAULT; + if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) + dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; + wpas_wps_start_pin(wpa_s, res->peer_interface_addr, + wpa_s->p2p_pin, 1, dev_pw_id); + } +} + + +static void p2p_go_configured(void *ctx, void *data) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_go_neg_results *params = data; + struct wpa_ssid *ssid; + int network_id = -1; + + ssid = wpa_s->current_ssid; + if (ssid && ssid->mode == WPAS_MODE_P2P_GO) { + wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning"); + if (wpa_s->global->p2p_group_formation == wpa_s) + wpa_s->global->p2p_group_formation = NULL; + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " + "go_dev_addr=" MACSTR "%s", + wpa_s->ifname, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->frequency, + params->passphrase ? params->passphrase : "", + MAC2STR(wpa_s->global->p2p_dev_addr), + params->persistent_group ? " [PERSISTENT]" : ""); + + if (params->persistent_group) + network_id = wpas_p2p_store_persistent_group( + wpa_s->parent, ssid, + wpa_s->global->p2p_dev_addr); + if (network_id < 0) + network_id = ssid->id; + wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + wpas_p2p_cross_connect_setup(wpa_s); + wpas_p2p_set_group_idle_timeout(wpa_s); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning"); + if (wpa_supplicant_ap_mac_addr_filter(wpa_s, + params->peer_interface_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address " + "filtering"); + return; + } + if (params->wps_method == WPS_PBC) + wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, + params->peer_device_addr); + else if (wpa_s->p2p_pin[0]) + wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, + wpa_s->p2p_pin, NULL, 0); + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; +} + + +static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int group_formation) +{ + struct wpa_ssid *ssid; + + if (wpas_copy_go_neg_results(wpa_s, params) < 0) + return; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return; + + wpa_s->show_group_started = 0; + + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->p2p_group = 1; + ssid->p2p_persistent_group = params->persistent_group; + ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : + WPAS_MODE_P2P_GO; + ssid->frequency = params->freq; + ssid->ssid = os_zalloc(params->ssid_len + 1); + if (ssid->ssid) { + os_memcpy(ssid->ssid, params->ssid, params->ssid_len); + ssid->ssid_len = params->ssid_len; + } + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->proto = WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->passphrase = os_strdup(params->passphrase); + + wpa_s->ap_configured_cb = p2p_go_configured; + wpa_s->ap_configured_cb_ctx = wpa_s; + wpa_s->ap_configured_cb_data = wpa_s->go_params; + wpa_s->connect_without_scan = ssid; + wpa_s->reassociate = 1; + wpa_s->disconnected = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_p2p_clone_config(struct wpa_supplicant *dst, + const struct wpa_supplicant *src) +{ + struct wpa_config *d; + const struct wpa_config *s; + + d = dst->conf; + s = src->conf; + +#define C(n) if (s->n) d->n = os_strdup(s->n) + C(device_name); + C(manufacturer); + C(model_name); + C(model_number); + C(serial_number); + C(config_methods); +#undef C + + os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN); + os_memcpy(d->sec_device_type, s->sec_device_type, + sizeof(d->sec_device_type)); + d->num_sec_device_types = s->num_sec_device_types; + + d->p2p_group_idle = s->p2p_group_idle; + d->p2p_intra_bss = s->p2p_intra_bss; + d->persistent_reconnect = s->persistent_reconnect; +} + + +static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type) +{ + char ifname[120], force_ifname[120]; + + if (wpa_s->pending_interface_name[0]) { + wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists " + "- skip creation of a new one"); + if (is_zero_ether_addr(wpa_s->pending_interface_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Pending virtual address " + "unknown?! ifname='%s'", + wpa_s->pending_interface_name); + return -1; + } + return 0; + } + + os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname, + wpa_s->p2p_group_idx); + if (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ) { + /* Try to avoid going over the IFNAMSIZ length limit */ + os_snprintf(ifname, sizeof(ifname), "p2p-%d", + wpa_s->p2p_group_idx); + } + force_ifname[0] = '\0'; + + wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", + ifname); + wpa_s->p2p_group_idx++; + + wpa_s->pending_interface_type = type; + if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname, + wpa_s->pending_interface_addr, NULL) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to create new group " + "interface"); + return -1; + } + + if (force_ifname[0]) { + wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s", + force_ifname); + os_strlcpy(wpa_s->pending_interface_name, force_ifname, + sizeof(wpa_s->pending_interface_name)); + } else + os_strlcpy(wpa_s->pending_interface_name, ifname, + sizeof(wpa_s->pending_interface_name)); + wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr " + MACSTR, wpa_s->pending_interface_name, + MAC2STR(wpa_s->pending_interface_addr)); + + return 0; +} + + +static void wpas_p2p_remove_pending_group_interface( + struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->pending_interface_name[0] || + is_zero_ether_addr(wpa_s->pending_interface_addr)) + return; /* No pending virtual interface */ + + wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s", + wpa_s->pending_interface_name); + wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type, + wpa_s->pending_interface_name); + os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); + wpa_s->pending_interface_name[0] = '\0'; +} + + +static struct wpa_supplicant * +wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) +{ + struct wpa_interface iface; + struct wpa_supplicant *group_wpa_s; + + if (!wpa_s->pending_interface_name[0]) { + wpa_printf(MSG_ERROR, "P2P: No pending group interface"); + if (!wpas_p2p_create_iface(wpa_s)) + return NULL; + /* + * Something has forced us to remove the pending interface; try + * to create a new one and hope for the best that we will get + * the same local address. + */ + if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT) < 0) + return NULL; + } + + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + iface.driver_param = wpa_s->conf->driver_param; + group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (group_wpa_s == NULL) { + wpa_printf(MSG_ERROR, "P2P: Failed to create new " + "wpa_supplicant interface"); + return NULL; + } + wpa_s->pending_interface_name[0] = '\0'; + group_wpa_s->parent = wpa_s; + group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO : + P2P_GROUP_INTERFACE_CLIENT; + wpa_s->global->p2p_group_formation = group_wpa_s; + + wpas_p2p_clone_config(group_wpa_s, wpa_s); + + return group_wpa_s; +} + + +static void wpas_p2p_group_formation_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); + if (wpa_s->global->p2p) + p2p_group_formation_failed(wpa_s->global->p2p); + else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + wpa_drv_p2p_group_formation_failed(wpa_s); + wpas_group_formation_completed(wpa_s, 0); +} + + +void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + + if (res->status) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", + res->status); + wpas_notify_p2p_go_neg_completed(wpa_s, res); + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); + wpas_notify_p2p_go_neg_completed(wpa_s, res); + + if (wpa_s->create_p2p_iface) { + struct wpa_supplicant *group_wpa_s = + wpas_p2p_init_group_interface(wpa_s, res->role_go); + if (group_wpa_s == NULL) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + if (group_wpa_s != wpa_s) { + os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, + sizeof(group_wpa_s->p2p_pin)); + group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; + } + os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); + wpa_s->pending_interface_name[0] = '\0'; + group_wpa_s->p2p_in_provisioning = 1; + + if (res->role_go) + wpas_start_wps_go(group_wpa_s, res, 1); + else + wpas_start_wps_enrollee(group_wpa_s, res); + } else { + wpa_s->p2p_in_provisioning = 1; + wpa_s->global->p2p_group_formation = wpa_s; + + if (res->role_go) + wpas_start_wps_go(wpa_s, res, 1); + else + wpas_start_wps_enrollee(ctx, res); + } + + wpa_s->p2p_long_listen = 0; + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); + eloop_register_timeout(15 + res->peer_config_timeout / 100, + (res->peer_config_timeout % 100) * 10000, + wpas_p2p_group_formation_timeout, wpa_s, NULL); +} + + +void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR + " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); + + wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id); +} + + +void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device) +{ + struct wpa_supplicant *wpa_s = ctx; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR + " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x", + MAC2STR(addr), MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, devtype, + sizeof(devtype)), + info->device_name, info->config_methods, + info->dev_capab, info->group_capab); + + wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); +} + + +static void wpas_dev_lost(void *ctx, const u8 *dev_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST + "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr)); + + wpas_notify_p2p_device_lost(wpa_s, dev_addr); +} + + +static int wpas_start_listen(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL); + + if (wpa_drv_probe_req_report(wpa_s, 1) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to " + "report received Probe Request frames"); + return -1; + } + + wpa_s->pending_listen_freq = freq; + wpa_s->pending_listen_duration = duration; + + if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " + "to remain on channel (%u MHz) for Listen " + "state", freq); + wpa_s->pending_listen_freq = 0; + return -1; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = freq; + + return 0; +} + + +static void wpas_stop_listen(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); + wpa_drv_probe_req_report(wpa_s, 0); +} + + +static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); +} + + +static struct p2p_srv_bonjour * +wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + size_t len; + + len = wpabuf_len(query); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (len == wpabuf_len(bsrv->query) && + os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), + len) == 0) + return bsrv; + } + return NULL; +} + + +static struct p2p_srv_upnp * +wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version == usrv->version && + os_strcmp(service, usrv->service) == 0) + return usrv; + } + return NULL; +} + + +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + u8 *len_pos; + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, srv_proto); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (wpabuf_tailroom(resp) < + 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_bonjour *bsrv; + struct wpabuf buf; + u8 *len_pos; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", + query, query_len); + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + wpabuf_set(&buf, query, query_len); + bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf); + if (bsrv == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " + "available"); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + return; + } + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp)); + + if (wpabuf_tailroom(resp) >= + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) { + /* Response Data */ + wpabuf_put_buf(resp, bsrv->query); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + } + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + return; + } + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, usrv->version); + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + wpabuf_put_str(resp, usrv->service); + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + u8 version; + char *str; + int count = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", + query, query_len); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + version = query[0]; + str = os_malloc(query_len); + if (str == NULL) + return; + os_memcpy(str, query + 1, query_len - 1); + str[query_len - 1] = '\0'; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version != usrv->version) + continue; + + if (os_strcmp(str, "ssdp:all") != 0 && + os_strstr(usrv->service, str) == NULL) + continue; + + if (wpabuf_tailroom(resp) < 2) + break; + if (count == 0) { + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, version); + } else + wpabuf_put_u8(resp, ','); + + count++; + + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) + break; + wpabuf_put_str(resp, usrv->service); + } + os_free(str); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " + "available"); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + struct wpabuf *resp; + u8 srv_proto, srv_trans_id; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", + tlvs, tlvs_len); + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " + MACSTR " %u %u %s", + freq, MAC2STR(sa), dialog_token, update_indic, + buf); + os_free(buf); + } + + if (wpa_s->p2p_sd_over_ctrl_iface) { + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + return; /* to be processed by an external program */ + } + + resp = wpabuf_alloc(10000); + if (resp == NULL) + return; + + while (pos + 1 < end) { + wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 2) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " + "length"); + wpabuf_free(resp); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", + pos, tlv_end - pos); + + + if (wpa_s->force_long_sd) { + wpa_printf(MSG_DEBUG, "P2P: SD test - force long " + "response"); + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + goto done; + } + + switch (srv_proto) { + case P2P_SERV_ALL_SERVICES: + wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " + "for all services"); + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && + dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: No service " + "discovery protocols available"); + wpas_sd_add_proto_not_avail( + resp, P2P_SERV_ALL_SERVICES, + srv_trans_id); + break; + } + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + break; + case P2P_SERV_BONJOUR: + wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + case P2P_SERV_UPNP: + wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Unavailable service " + "protocol %u", srv_proto); + wpas_sd_add_proto_not_avail(resp, srv_proto, + srv_trans_id); + break; + } + + pos = tlv_end; + } + +done: + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + + wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp); + + wpabuf_free(resp); +} + + +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs", + tlvs, tlvs_len); + if (tlvs_len > 1500) { + /* TODO: better way for handling this */ + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR + " %u ", + MAC2STR(sa), update_indic, + (unsigned int) tlvs_len); + } else { + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, + P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s", + MAC2STR(sa), update_indic, buf); + os_free(buf); + } + } + + while (pos < end) { + u8 srv_proto, srv_trans_id, status; + + wpa_printf(MSG_DEBUG, "P2P: Service Response TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " + "length"); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + status = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u", + status); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", + pos, tlv_end - pos); + + pos = tlv_end; + } + + wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len); +} + + +void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return (void *) wpa_drv_p2p_sd_request(wpa_s, dst, tlvs); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return NULL; + return p2p_sd_request(wpa_s->global->p2p, dst, tlvs); +} + + +void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) +{ + struct wpabuf *tlvs; + void *ret; + + tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); + if (tlvs == NULL) + return NULL; + wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); + wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, version); + wpabuf_put_str(tlvs, query); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_sd_cancel_request(wpa_s, (u64) req); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + return p2p_sd_cancel_request(wpa_s->global->p2p, req); +} + + +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token, + resp_tlvs); + return; + } + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, + resp_tlvs); +} + + +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + wpa_drv_p2p_service_update(wpa_s); + return; + } + if (wpa_s->global->p2p) + p2p_sd_service_update(wpa_s->global->p2p); +} + + +static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv) +{ + dl_list_del(&bsrv->list); + wpabuf_free(bsrv->query); + wpabuf_free(bsrv->resp); + os_free(bsrv); +} + + +static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv) +{ + dl_list_del(&usrv->list); + os_free(usrv->service); + os_free(usrv); +} + + +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) +{ + struct p2p_srv_bonjour *bsrv, *bn; + struct p2p_srv_upnp *usrv, *un; + + dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) + wpas_p2p_srv_bonjour_free(bsrv); + + dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) + wpas_p2p_srv_upnp_free(usrv); + + wpas_p2p_sd_service_update(wpa_s); +} + + +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv) { + wpabuf_free(query); + wpabuf_free(bsrv->resp); + bsrv->resp = resp; + return 0; + } + + bsrv = os_zalloc(sizeof(*bsrv)); + if (bsrv == NULL) + return -1; + bsrv->query = query; + bsrv->resp = resp; + dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + + bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); + if (bsrv == NULL) + return -1; + wpas_p2p_srv_bonjour_free(bsrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + if (wpas_p2p_service_get_upnp(wpa_s, version, service)) + return 0; /* Already listed */ + usrv = os_zalloc(sizeof(*usrv)); + if (usrv == NULL) + return -1; + usrv->version = version; + usrv->service = os_strdup(service); + if (usrv->service == NULL) { + os_free(usrv); + return -1; + } + dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list); + + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + usrv = wpas_p2p_service_get_upnp(wpa_s, version, service); + if (usrv == NULL) + return -1; + wpas_p2p_srv_upnp_free(usrv); + wpas_p2p_sd_service_update(wpa_s); + return 0; +} + + +static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s, + const u8 *peer, const char *params, + unsigned int generated_pin) +{ + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s", + MAC2STR(peer), generated_pin, params); +} + + +static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s, + const u8 *peer, const char *params) +{ + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s", + MAC2STR(peer), params); +} + + +void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, const u8 *group_id, + size_t group_id_len) +{ + struct wpa_supplicant *wpa_s = ctx; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + char params[300]; + u8 empty_dev_type[8]; + unsigned int generated_pin = 0; + struct wpa_supplicant *group = NULL; + + if (group_id) { + for (group = wpa_s->global->ifaces; group; group = group->next) + { + struct wpa_ssid *s = group->current_ssid; + if (s != NULL && + s->mode == WPAS_MODE_P2P_GO && + group_id_len - ETH_ALEN == s->ssid_len && + os_memcmp(group_id + ETH_ALEN, s->ssid, + s->ssid_len) == 0) + break; + } + } + + if (pri_dev_type == NULL) { + os_memset(empty_dev_type, 0, sizeof(empty_dev_type)); + pri_dev_type = empty_dev_type; + } + os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x%s%s", + MAC2STR(dev_addr), + wps_dev_type_bin2str(pri_dev_type, devtype, + sizeof(devtype)), + dev_name, supp_config_methods, dev_capab, group_capab, + group ? " group=" : "", + group ? group->ifname : ""); + params[sizeof(params) - 1] = '\0'; + + if (config_methods & WPS_CONFIG_DISPLAY) { + generated_pin = wps_generate_pin(); + wpas_prov_disc_local_display(wpa_s, peer, params, + generated_pin); + } else if (config_methods & WPS_CONFIG_KEYPAD) + wpas_prov_disc_local_keypad(wpa_s, peer, params); + else if (config_methods & WPS_CONFIG_PUSHBUTTON) + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR + "%s", MAC2STR(peer), params); + + wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */, + P2P_PROV_DISC_SUCCESS, + config_methods, generated_pin); +} + + +void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +{ + struct wpa_supplicant *wpa_s = ctx; + unsigned int generated_pin = 0; + + if (wpa_s->pending_pd_before_join && + (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || + os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation"); + wpas_p2p_join_start(wpa_s); + return; + } + + if (config_methods & WPS_CONFIG_DISPLAY) + wpas_prov_disc_local_keypad(wpa_s, peer, ""); + else if (config_methods & WPS_CONFIG_KEYPAD) { + generated_pin = wps_generate_pin(); + wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin); + } else if (config_methods & WPS_CONFIG_PUSHBUTTON) + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR, + MAC2STR(peer)); + + wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_SUCCESS, + config_methods, generated_pin); +} + + +void wpas_prov_disc_fail(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, + status, 0, 0); +} + + +static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *go_dev_addr, const u8 *ssid, + size_t ssid_len, int *go, u8 *group_bssid, + int *force_freq, int persistent_group) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + u8 cur_bssid[ETH_ALEN]; + int res; + struct wpa_supplicant *grp; + + if (!persistent_group) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR + " to join an active group", MAC2STR(sa)); + if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && + (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN) + == 0 || + os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) { + wpa_printf(MSG_DEBUG, "P2P: Accept previously " + "authorized invitation"); + goto accept_inv; + } + /* + * Do not accept the invitation automatically; notify user and + * request approval. + */ + return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + } + + grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go); + if (grp) { + wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already " + "running persistent group"); + if (*go) + os_memcpy(group_bssid, grp->own_addr, ETH_ALEN); + goto accept_inv; + } + + if (!wpa_s->conf->persistent_reconnect) + return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 && + s->ssid_len == ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + } + + if (!s) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR + " requested reinvocation of an unknown group", + MAC2STR(sa)); + return P2P_SC_FAIL_UNKNOWN_GROUP; + } + + if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) { + *go = 1; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_printf(MSG_DEBUG, "P2P: The only available " + "interface is already in use - reject " + "invitation"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); + } else if (s->mode == WPAS_MODE_P2P_GO) { + *go = 1; + if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) + { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface address for the group"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + os_memcpy(group_bssid, wpa_s->pending_interface_addr, + ETH_ALEN); + } + +accept_inv: + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 && + wpa_s->assoc_freq) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " + "the channel we are already using"); + *force_freq = wpa_s->assoc_freq; + } + + res = wpa_drv_shared_freq(wpa_s); + if (res > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " + "with the channel we are already using on a " + "shared interface"); + *force_freq = res; + } + + return P2P_SC_SUCCESS; +} + + +static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && + s->ssid_len == ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + } + + if (status == P2P_SC_SUCCESS) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR + " was accepted; op_freq=%d MHz", + MAC2STR(sa), op_freq); + if (s) { + wpas_p2p_group_add_persistent( + wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0); + } else if (bssid) { + wpas_p2p_join(wpa_s, bssid, go_dev_addr, + wpa_s->p2p_wps_method); + } + return; + } + + if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR + " was rejected (status %u)", MAC2STR(sa), status); + return; + } + + if (!s) { + if (bssid) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " bssid=" MACSTR " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr), + MAC2STR(bssid)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr)); + } + return; + } + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR + " persistent=%d", MAC2STR(sa), s->id); +} + + +static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *ssid; + + if (bssid) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d " MACSTR, + status, MAC2STR(bssid)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d ", status); + } + wpas_notify_p2p_invitation_result(wpa_s, status, bssid); + + if (wpa_s->pending_invite_ssid_id == -1) + return; /* Invitation to active group */ + + if (status != P2P_SC_SUCCESS) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return; + } + + ssid = wpa_config_get_network(wpa_s->conf, + wpa_s->pending_invite_ssid_id); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "P2P: Could not find persistent group " + "data matching with invitation"); + return; + } + + wpas_p2p_group_add_persistent(wpa_s, ssid, + ssid->mode == WPAS_MODE_P2P_GO, 0); +} + + +static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + int i, cla = 0; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " + "band"); + + /* Operating class 81 - 2.4 GHz band channels 1..13 */ + chan->reg_class[cla].reg_class = 81; + chan->reg_class[cla].channels = 11; + for (i = 0; i < 11; i++) + chan->reg_class[cla].channel[i] = i + 1; + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz " + "band"); + + /* Operating class 115 - 5 GHz, channels 36-48 */ + chan->reg_class[cla].reg_class = 115; + chan->reg_class[cla].channels = 4; + chan->reg_class[cla].channel[0] = 36; + chan->reg_class[cla].channel[1] = 40; + chan->reg_class[cla].channel[2] = 44; + chan->reg_class[cla].channel[3] = 48; + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz " + "band"); + + /* Operating class 124 - 5 GHz, channels 149,153,157,161 */ + chan->reg_class[cla].reg_class = 124; + chan->reg_class[cla].channels = 4; + chan->reg_class[cla].channel[0] = 149; + chan->reg_class[cla].channel[1] = 153; + chan->reg_class[cla].channel[2] = 157; + chan->reg_class[cla].channel[3] = 161; + cla++; + + chan->reg_classes = cla; + return 0; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].chan == chan) { + if (flags) + *flags = mode->channels[i].flag; + return !(mode->channels[i].flag & + (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_RADAR)); + } + } + + return 0; +} + + +struct p2p_oper_class_map { + enum hostapd_hw_mode mode; + u8 op_class; + u8 min_chan; + u8 max_chan; + u8 inc; + enum { BW20, BW40PLUS, BW40MINUS } bw; +}; + +static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + struct hostapd_hw_modes *mode; + int cla, op; + struct p2p_oper_class_map op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, + { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 }, +#if 0 /* Do not enable HT40 on 2 GHz for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, +#endif + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, + { -1, 0, 0, 0, 0, BW20 } + }; + + if (wpa_s->hw.modes == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " + "of all supported channels; assume dualband " + "support"); + return wpas_p2p_default_channels(wpa_s, chan); + } + + cla = 0; + + for (op = 0; op_class[op].op_class; op++) { + struct p2p_oper_class_map *o = &op_class[op]; + u8 ch; + struct p2p_reg_class *reg = NULL; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); + if (mode == NULL) + continue; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + int flag; + if (!has_channel(mode, ch, &flag)) + continue; + if (o->bw == BW40MINUS && + (!(flag & HOSTAPD_CHAN_HT40MINUS) || + !has_channel(mode, ch - 4, NULL))) + continue; + if (o->bw == BW40PLUS && + (!(flag & HOSTAPD_CHAN_HT40PLUS) || + !has_channel(mode, ch + 4, NULL))) + continue; + if (reg == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Add operating " + "class %u", o->op_class); + reg = &chan->reg_class[cla]; + cla++; + reg->reg_class = o->op_class; + } + reg->channel[reg->channels] = ch; + reg->channels++; + } + if (reg) { + wpa_hexdump(MSG_DEBUG, "P2P: Channels", + reg->channel, reg->channels); + } + } + + chan->reg_classes = cla; + + return 0; +} + + +static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0) + break; + } + if (wpa_s == NULL) + return -1; + + return wpa_drv_get_noa(wpa_s, buf, buf_len); +} + + +static int wpas_go_connected(void *ctx, const u8 *dev_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (ssid == NULL) + continue; + if (ssid->mode != WPAS_MODE_INFRA) + continue; + if (wpa_s->wpa_state != WPA_COMPLETED && + wpa_s->wpa_state != WPA_GROUP_HANDSHAKE) + continue; + if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +/** + * wpas_p2p_init - Initialize P2P module for %wpa_supplicant + * @global: Pointer to global data from wpa_supplicant_init() + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * Returns: 0 on success, -1 on failure + */ +int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) +{ + struct p2p_config p2p; + unsigned int r; + int i; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + return 0; + + if (global->p2p) + return 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + struct p2p_params params; + + wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management"); + os_memset(¶ms, 0, sizeof(params)); + params.dev_name = wpa_s->conf->device_name; + os_memcpy(params.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + params.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(params.sec_dev_type, + wpa_s->conf->sec_device_type, + params.num_sec_dev_types * WPS_DEV_TYPE_LEN); + + if (wpa_drv_p2p_set_params(wpa_s, ¶ms) < 0) + return -1; + + return 0; + } + + os_memset(&p2p, 0, sizeof(p2p)); + p2p.msg_ctx = wpa_s; + p2p.cb_ctx = wpa_s; + p2p.p2p_scan = wpas_p2p_scan; + p2p.send_action = wpas_send_action; + p2p.send_action_done = wpas_send_action_done; + p2p.go_neg_completed = wpas_go_neg_completed; + p2p.go_neg_req_rx = wpas_go_neg_req_rx; + p2p.dev_found = wpas_dev_found; + p2p.dev_lost = wpas_dev_lost; + p2p.start_listen = wpas_start_listen; + p2p.stop_listen = wpas_stop_listen; + p2p.send_probe_resp = wpas_send_probe_resp; + p2p.sd_request = wpas_sd_request; + p2p.sd_response = wpas_sd_response; + p2p.prov_disc_req = wpas_prov_disc_req; + p2p.prov_disc_resp = wpas_prov_disc_resp; + p2p.prov_disc_fail = wpas_prov_disc_fail; + p2p.invitation_process = wpas_invitation_process; + p2p.invitation_received = wpas_invitation_received; + p2p.invitation_result = wpas_invitation_result; + p2p.get_noa = wpas_get_noa; + p2p.go_connected = wpas_go_connected; + + os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); + os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); + p2p.dev_name = wpa_s->conf->device_name; + p2p.manufacturer = wpa_s->conf->manufacturer; + p2p.model_name = wpa_s->conf->model_name; + p2p.model_number = wpa_s->conf->model_number; + p2p.serial_number = wpa_s->conf->serial_number; + if (wpa_s->wps) { + os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16); + p2p.config_methods = wpa_s->wps->config_methods; + } + + if (wpa_s->conf->p2p_listen_reg_class && + wpa_s->conf->p2p_listen_channel) { + p2p.reg_class = wpa_s->conf->p2p_listen_reg_class; + p2p.channel = wpa_s->conf->p2p_listen_channel; + } else { + p2p.reg_class = 81; + /* + * Pick one of the social channels randomly as the listen + * channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.channel = 1 + (r % 3) * 5; + } + wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel); + + if (wpa_s->conf->p2p_oper_reg_class && + wpa_s->conf->p2p_oper_channel) { + p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class; + p2p.op_channel = wpa_s->conf->p2p_oper_channel; + p2p.cfg_op_channel = 1; + wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: " + "%d:%d", p2p.op_reg_class, p2p.op_channel); + + } else { + p2p.op_reg_class = 81; + /* + * Use random operation channel from (1, 6, 11) if no other + * preference is indicated. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.op_channel = 1 + (r % 3) * 5; + p2p.cfg_op_channel = 0; + wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " + "%d:%d", p2p.op_reg_class, p2p.op_channel); + } + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + os_memcpy(p2p.country, wpa_s->conf->country, 2); + p2p.country[2] = 0x04; + } else + os_memcpy(p2p.country, "XX\x04", 3); + + if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) { + wpa_printf(MSG_ERROR, "P2P: Failed to configure supported " + "channel list"); + return -1; + } + + os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type, + p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN); + + p2p.concurrent_operations = !!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_P2P_CONCURRENT); + + p2p.max_peers = 100; + + if (wpa_s->conf->p2p_ssid_postfix) { + p2p.ssid_postfix_len = + os_strlen(wpa_s->conf->p2p_ssid_postfix); + if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix)) + p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix); + os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix, + p2p.ssid_postfix_len); + } + + p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss; + + global->p2p = p2p_init(&p2p); + if (global->p2p == NULL) + return -1; + + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + global->p2p, wpa_s->conf->wps_vendor_ext[i]); + } + + return 0; +} + + +/** + * wpas_p2p_deinit - Deinitialize per-interface P2P data + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * + * This function deinitialize per-interface P2P data. + */ +void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver && wpa_s->drv_priv) + wpa_drv_probe_req_report(wpa_s, 0); + + if (wpa_s->go_params) { + /* Clear any stored provisioning info */ + p2p_clear_provisioning_info( + wpa_s->global->p2p, + wpa_s->go_params->peer_device_addr); + } + + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL); + wpa_s->p2p_long_listen = 0; + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + wpas_p2p_remove_pending_group_interface(wpa_s); + + /* TODO: remove group interface from the driver if this wpa_s instance + * is on top of a P2P group interface */ +} + + +/** + * wpas_p2p_deinit_global - Deinitialize global P2P module + * @global: Pointer to global data from wpa_supplicant_init() + * + * This function deinitializes the global (per device) P2P module. + */ +void wpas_p2p_deinit_global(struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s, *tmp; + + if (global->p2p == NULL) + return; + + /* Remove remaining P2P group interfaces */ + wpa_s = global->ifaces; + if (wpa_s) + wpas_p2p_service_flush(wpa_s); + while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) + wpa_s = wpa_s->next; + while (wpa_s) { + enum wpa_driver_if_type type; + tmp = global->ifaces; + while (tmp && + (tmp == wpa_s || + tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) { + tmp = tmp->next; + } + if (tmp == NULL) + break; + type = wpas_p2p_if_type(tmp->p2p_group_interface); + /* Disconnect from the P2P group and deinit the interface */ + wpas_p2p_disconnect(tmp); + } + + /* + * Deinit GO data on any possibly remaining interface (if main + * interface is used as GO). + */ + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->ap_iface) + wpas_p2p_group_deinit(wpa_s); + } + + p2p_deinit(global->p2p); + global->p2p = NULL; +} + + +static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_flags & + (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | + WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P)) + return 1; /* P2P group requires a new interface in every case + */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT)) + return 0; /* driver does not support concurrent operations */ + if (wpa_s->global->ifaces->next) + return 1; /* more that one interface already in use */ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + return 1; /* this interface is already in use */ + return 0; +} + + +static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + if (persistent_group && wpa_s->conf->persistent_reconnect) + persistent_group = 2; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method, + go_intent, own_interface_addr, + force_freq, persistent_group); + } + + return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group); +} + + +static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group) +{ + if (persistent_group && wpa_s->conf->persistent_reconnect) + persistent_group = 2; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + + return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group); +} + + +static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) +{ + wpa_s->p2p_join_scan_count++; + wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d", + wpa_s->p2p_join_scan_count); + if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) { + wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR + " for join operationg - stop join attempt", + MAC2STR(wpa_s->pending_join_iface_addr)); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + } +} + + +static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (!wpa_s->pending_pd_before_join) + return; + /* + * Provision Discovery Response may have been lost - try to connect + * anyway since we do not need any information from this PD. + */ + wpa_printf(MSG_DEBUG, "P2P: PD timeout for join-existing-group - " + "try to connect anyway"); + wpas_p2p_join_start(wpa_s); +} + + +static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + struct wpa_bss *bss; + int freq; + u8 iface_addr[ETH_ALEN]; + + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + + if (wpa_s->global->p2p_disabled) + return; + + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join", + scan_res ? (int) scan_res->num : -1); + + if (scan_res) + wpas_p2p_scan_res_handler(wpa_s, scan_res); + + freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + if (freq < 0 && + p2p_get_interface_addr(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, + iface_addr) == 0 && + os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface " + "address for join from " MACSTR " to " MACSTR + " based on newly discovered P2P peer entry", + MAC2STR(wpa_s->pending_join_iface_addr), + MAC2STR(iface_addr)); + os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, + ETH_ALEN); + + freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + } + if (freq >= 0) { + wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " + "from P2P peer table: %d MHz", freq); + } + bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr); + if (bss) { + freq = bss->freq; + wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " + "from BSS table: %d MHz", freq); + } + if (freq > 0) { + u16 method; + + wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request " + "prior to joining an existing group (GO " MACSTR + " freq=%u MHz)", + MAC2STR(wpa_s->pending_join_dev_addr), freq); + wpa_s->pending_pd_before_join = 1; + + switch (wpa_s->pending_join_wps_method) { + case WPS_PIN_DISPLAY: + method = WPS_CONFIG_KEYPAD; + break; + case WPS_PIN_KEYPAD: + method = WPS_CONFIG_DISPLAY; + break; + case WPS_PBC: + method = WPS_CONFIG_PUSHBUTTON; + break; + default: + method = 0; + break; + } + + if ((p2p_get_provisioning_info(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr) == + method)) { + /* + * We have already performed provision discovery for + * joining the group. Proceed directly to join + * operation without duplicated provision discovery. */ + wpa_printf(MSG_DEBUG, "P2P: Provisioning discovery " + "with " MACSTR " already done - proceed to " + "join", + MAC2STR(wpa_s->pending_join_dev_addr)); + wpa_s->pending_pd_before_join = 0; + goto start; + } + + if (p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, method, 1, + freq) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " + "Discovery Request before joining an " + "existing group"); + wpa_s->pending_pd_before_join = 0; + goto start; + } + + /* + * Actual join operation will be started from the Action frame + * TX status callback (if no ACK is received) or when the + * Provision Discovery Response is received. Use a short + * timeout as a backup mechanism should the Provision Discovery + * Response be lost for any reason. + */ + eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, + NULL); + eloop_register_timeout(2, 0, wpas_p2p_pd_before_join_timeout, + wpa_s, NULL); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + return; + +start: + /* Start join operation immediately */ + wpas_p2p_join_start(wpa_s); +} + + +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + int ret; + struct wpa_driver_scan_params params; + struct wpabuf *wps_ie, *ies; + size_t ielen; + + os_memset(¶ms, 0, sizeof(params)); + + /* P2P Wildcard SSID */ + params.num_ssids = 1; + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, + WPS_REQ_ENROLLEE, 0, NULL); + if (wps_ie == NULL) { + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(wpa_s->global->p2p, ies, NULL); + + params.p2p_probe = 1; + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + /* + * Run a scan to update BSS table and start Provision Discovery once + * the new scan results become available. + */ + wpa_s->scan_res_handler = wpas_p2p_scan_res_join; + ret = wpa_drv_scan(wpa_s, ¶ms); + + wpabuf_free(ies); + + if (ret) { + wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - " + "try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + } +} + + +static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, + const u8 *dev_addr, enum p2p_wps_method wps_method) +{ + wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " + MACSTR " dev " MACSTR ")", + MAC2STR(iface_addr), MAC2STR(dev_addr)); + + os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); + os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); + wpa_s->pending_join_wps_method = wps_method; + + /* Make sure we are not running find during connection establishment */ + wpas_p2p_stop_find(wpa_s); + + wpa_s->p2p_join_scan_count = 0; + wpas_p2p_join_scan(wpa_s, NULL); + return 0; +} + + +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *group; + struct p2p_go_neg_results res; + + eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL); + group = wpas_p2p_get_group_iface(wpa_s, 0, 0); + if (group == NULL) + return -1; + if (group != wpa_s) { + os_memcpy(group->p2p_pin, wpa_s->p2p_pin, + sizeof(group->p2p_pin)); + group->p2p_wps_method = wpa_s->p2p_wps_method; + } + + group->p2p_in_provisioning = 1; + + os_memset(&res, 0, sizeof(res)); + os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, + ETH_ALEN); + res.wps_method = wpa_s->pending_join_wps_method; + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to " + "starting client"); + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + wpas_start_wps_enrollee(group, &res); + + /* + * Allow a longer timeout for join-a-running-group than normal 15 + * second group formation timeout since the GO may not have authorized + * our connection yet. + */ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); + eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout, + wpa_s, NULL); + + return 0; +} + + +/** + * wpas_p2p_connect - Request P2P Group Formation to be started + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @peer_addr: Address of the peer P2P Device + * @pin: PIN to use during provisioning or %NULL to indicate PBC mode + * @persistent_group: Whether to create a persistent group + * @join: Whether to join an existing group (as a client) instead of starting + * Group Owner negotiation; @peer_addr is BSSID in that case + * @auth: Whether to only authorize the connection instead of doing that and + * initiating Group Owner negotiation + * @go_intent: GO Intent or -1 to use default + * @freq: Frequency for the group or 0 for auto-selection + * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified + * failure, -2 on failure due to channel not currently available, + * -3 if forced channel is not supported + */ +int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *pin, enum p2p_wps_method wps_method, + int persistent_group, int join, int auth, int go_intent, + int freq) +{ + int force_freq = 0, oper_freq = 0; + u8 bssid[ETH_ALEN]; + int ret = 0; + enum wpa_driver_if_type iftype; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (go_intent < 0) + go_intent = wpa_s->conf->p2p_go_intent; + + if (!auth) + wpa_s->p2p_long_listen = 0; + + wpa_s->p2p_wps_method = wps_method; + + if (pin) + os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); + else if (wps_method == WPS_PIN_DISPLAY) { + ret = wps_generate_pin(); + os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", + ret); + wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", + wpa_s->p2p_pin); + } else + wpa_s->p2p_pin[0] = '\0'; + + if (join) { + u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; + if (auth) { + wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " + "connect a running group from " MACSTR, + MAC2STR(peer_addr)); + os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + return ret; + } + os_memcpy(dev_addr, peer_addr, ETH_ALEN); + if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr, + iface_addr) < 0) { + os_memcpy(iface_addr, peer_addr, ETH_ALEN); + p2p_get_dev_addr(wpa_s->global->p2p, peer_addr, + dev_addr); + } + if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) < + 0) + return -1; + return ret; + } + + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && + wpa_s->assoc_freq) + oper_freq = wpa_s->assoc_freq; + else { + oper_freq = wpa_drv_shared_freq(wpa_s); + if (oper_freq < 0) + oper_freq = 0; + } + + if (freq > 0) { + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel " + "(%u MHz) is not supported for P2P uses", + freq); + return -3; + } + + if (oper_freq > 0 && freq != oper_freq && + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " + "on %u MHz while connected on another " + "channel (%u MHz)", freq, oper_freq); + return -2; + } + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "requested channel (%u MHz)", freq); + force_freq = freq; + } else if (oper_freq > 0 && + !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) { + if (!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " + "while connected on non-P2P supported " + "channel (%u MHz)", oper_freq); + return -2; + } + wpa_printf(MSG_DEBUG, "P2P: Current operating channel " + "(%u MHz) not available for P2P - try to use " + "another channel", oper_freq); + force_freq = 0; + } else if (oper_freq > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "channel we are already using (%u MHz) on another " + "interface", oper_freq); + force_freq = oper_freq; + } + + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); + + if (!wpa_s->create_p2p_iface) { + if (auth) { + if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, + go_intent, wpa_s->own_addr, + force_freq, persistent_group) + < 0) + return -1; + return ret; + } + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, + go_intent, wpa_s->own_addr, + force_freq, persistent_group) < 0) + return -1; + return ret; + } + + /* Prepare to add a new interface for the group */ + iftype = WPA_IF_P2P_GROUP; + if (go_intent == 15) + iftype = WPA_IF_P2P_GO; + if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface for the group"); + return -1; + } + + if (auth) { + if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, + go_intent, + wpa_s->pending_interface_addr, + force_freq, persistent_group) < 0) + return -1; + return ret; + } + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent, + wpa_s->pending_interface_addr, + force_freq, persistent_group) < 0) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return -1; + } + return ret; +} + + +/** + * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @freq: Frequency of the channel in MHz + * @duration: Duration of the stay on the channel in milliseconds + * + * This callback is called when the driver indicates that it has started the + * requested remain-on-channel duration. + */ +void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { + p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, + wpa_s->pending_listen_duration); + wpa_s->pending_listen_freq = 0; + } +} + + +static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, + unsigned int timeout) +{ + /* Limit maximum Listen state time based on driver limitation. */ + if (timeout > wpa_s->max_remain_on_chan) + timeout = wpa_s->max_remain_on_chan; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_listen(wpa_s, timeout); + + return p2p_listen(wpa_s->global->p2p, timeout); +} + + +/** + * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @freq: Frequency of the channel in MHz + * + * This callback is called when the driver indicates that a remain-on-channel + * operation has been completed, i.e., the duration on the requested channel + * has timed out. + */ +void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " + "(p2p_long_listen=%d ms pending_action_tx=%p)", + wpa_s->p2p_long_listen, wpa_s->pending_action_tx); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) + return; /* P2P module started a new operation */ + if (wpa_s->pending_action_tx) + return; + if (wpa_s->p2p_long_listen > 0) + wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan; + if (wpa_s->p2p_long_listen > 0) { + wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); + wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); + } +} + + +/** + * wpas_p2p_group_remove - Remove a P2P group + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @ifname: Network interface name of the group interface or "*" to remove all + * groups + * Returns: 0 on success, -1 on failure + * + * This function is used to remove a P2P group. This can be used to disconnect + * from a group in which the local end is a P2P Client or to end a P2P Group in + * case the local end is the Group Owner. If a virtual network interface was + * created for this group, that interface will be removed. Otherwise, only the + * configured P2P group network will be removed from the interface. + */ +int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) +{ + struct wpa_global *global = wpa_s->global; + + if (os_strcmp(ifname, "*") == 0) { + struct wpa_supplicant *prev; + wpa_s = global->ifaces; + while (wpa_s) { + prev = wpa_s; + wpa_s = wpa_s->next; + wpas_p2p_disconnect(prev); + } + return 0; + } + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + break; + } + + return wpas_p2p_disconnect(wpa_s); +} + + +static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int freq) +{ + u8 bssid[ETH_ALEN]; + int res; + + os_memset(params, 0, sizeof(*params)); + params->role_go = 1; + if (freq) { + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " + "frequency %d MHz", freq); + params->freq = freq; + } else if (wpa_s->conf->p2p_oper_reg_class == 81 && + wpa_s->conf->p2p_oper_channel >= 1 && + wpa_s->conf->p2p_oper_channel <= 11) { + params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "frequency %d MHz", params->freq); + } else if (wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 124) { + params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " + "frequency %d MHz", params->freq); + } else if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_overall_freq > 0 && + p2p_supported_freq(wpa_s->global->p2p, + wpa_s->best_overall_freq)) { + params->freq = wpa_s->best_overall_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " + "channel %d MHz", params->freq); + } else if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_24_freq > 0 && + p2p_supported_freq(wpa_s->global->p2p, + wpa_s->best_24_freq)) { + params->freq = wpa_s->best_24_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " + "channel %d MHz", params->freq); + } else if (wpa_s->conf->p2p_oper_channel == 0 && + wpa_s->best_5_freq > 0 && + p2p_supported_freq(wpa_s->global->p2p, + wpa_s->best_5_freq)) { + params->freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " + "channel %d MHz", params->freq); + } else { + params->freq = 2412; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference " + "known)", params->freq); + } + + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && + wpa_s->assoc_freq && !freq) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " + "already using"); + params->freq = wpa_s->assoc_freq; + } + + res = wpa_drv_shared_freq(wpa_s); + if (res > 0 && !freq) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " + "already using on a shared interface"); + params->freq = res; + } else if (res > 0 && freq != res && + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz " + "while connected on another channel (%u MHz)", + freq, res); + return -1; + } + + return 0; +} + + +static struct wpa_supplicant * +wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, + int go) +{ + struct wpa_supplicant *group_wpa_s; + + if (!wpas_p2p_create_iface(wpa_s)) + return wpa_s; + + if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT) < 0) + return NULL; + group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); + if (group_wpa_s == NULL) { + wpas_p2p_remove_pending_group_interface(wpa_s); + return NULL; + } + + return group_wpa_s; +} + + +/** + * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @persistent_group: Whether to create a persistent group + * @freq: Frequency for the group or 0 to indicate no hardcoding + * Returns: 0 on success, -1 on failure + * + * This function creates a new P2P group with the local end as the Group Owner, + * i.e., without using Group Owner Negotiation. + */ +int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, + int freq) +{ + struct p2p_go_neg_results params; + unsigned int r; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + /* Make sure we are not running find during connection establishment */ + wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); + wpas_p2p_stop_find(wpa_s); + + if (freq == 2) { + wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " + "band"); + if (wpa_s->best_24_freq > 0 && + p2p_supported_freq(wpa_s->global->p2p, + wpa_s->best_24_freq)) { + freq = wpa_s->best_24_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 2412 + (r % 3) * 25; + wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq == 5) { + wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz " + "band"); + if (wpa_s->best_5_freq > 0 && + p2p_supported_freq(wpa_s->global->p2p, + wpa_s->best_5_freq)) { + freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 5180 + (r % 4) * 20; + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: Could not select " + "5 GHz channel for P2P group"); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " + "(%u MHz) is not supported for P2P uses", + freq); + return -1; + } + + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + return -1; + if (params.freq && + !p2p_supported_freq(wpa_s->global->p2p, params.freq)) { + wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO " + "(%u MHz) is not supported for P2P uses", + params.freq); + return -1; + } + p2p_go_params(wpa_s->global->p2p, ¶ms); + params.persistent_group = persistent_group; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1); + if (wpa_s == NULL) + return -1; + wpas_start_wps_go(wpa_s, ¶ms, 0); + + return 0; +} + + +static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, + struct wpa_ssid *params, int addr_allocated) +{ + struct wpa_ssid *ssid; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); + if (wpa_s == NULL) + return -1; + + wpa_supplicant_ap_deinit(wpa_s); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->proto = WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->group_cipher = WPA_CIPHER_CCMP; + ssid->key_mgmt = WPA_KEY_MGMT_PSK; + ssid->ssid = os_malloc(params->ssid_len); + if (ssid->ssid == NULL) { + wpa_config_remove_network(wpa_s->conf, ssid->id); + return -1; + } + os_memcpy(ssid->ssid, params->ssid, params->ssid_len); + ssid->ssid_len = params->ssid_len; + ssid->p2p_group = 1; + ssid->export_keys = 1; + if (params->psk_set) { + os_memcpy(ssid->psk, params->psk, 32); + ssid->psk_set = 1; + } + if (params->passphrase) + ssid->passphrase = os_strdup(params->passphrase); + + wpa_supplicant_select_network(wpa_s, ssid); + + wpa_s->show_group_started = 1; + + return 0; +} + + +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int addr_allocated, + int freq) +{ + struct p2p_go_neg_results params; + int go = 0; + + if (ssid->disabled != 2 || ssid->ssid == NULL) + return -1; + + if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) && + go == (ssid->mode == WPAS_MODE_P2P_GO)) { + wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is " + "already running"); + return 0; + } + + /* Make sure we are not running find during connection establishment */ + wpas_p2p_stop_find(wpa_s); + + if (ssid->mode == WPAS_MODE_INFRA) + return wpas_start_p2p_client(wpa_s, ssid, addr_allocated); + + if (ssid->mode != WPAS_MODE_P2P_GO) + return -1; + + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + return -1; + + params.role_go = 1; + if (ssid->passphrase == NULL || + os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { + wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent " + "group"); + return -1; + } + os_strlcpy(params.passphrase, ssid->passphrase, + sizeof(params.passphrase)); + os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len); + params.ssid_len = ssid->ssid_len; + params.persistent_group = 1; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1); + if (wpa_s == NULL) + return -1; + + wpas_start_wps_go(wpa_s, ¶ms, 0); + + return 0; +} + + +static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface) { + struct hostapd_data *hapd = wpa_s->ap_iface->bss[0]; + if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) { + wpabuf_free(beacon_ies); + wpabuf_free(proberesp_ies); + return; + } + if (beacon_ies) { + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = beacon_ies; + } + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = proberesp_ies; + } else { + wpabuf_free(beacon_ies); + wpabuf_free(proberesp_ies); + } + wpa_supplicant_ap_update_beacon(wpa_s); +} + + +static void wpas_p2p_idle_update(void *ctx, int idle) +{ + struct wpa_supplicant *wpa_s = ctx; + if (!wpa_s->ap_iface) + return; + wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not "); + if (idle) + wpas_p2p_set_group_idle_timeout(wpa_s); + else + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); +} + + +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, + int persistent_group, + int group_formation) +{ + struct p2p_group *group; + struct p2p_group_config *cfg; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return NULL; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return NULL; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return NULL; + + if (persistent_group && wpa_s->conf->persistent_reconnect) + cfg->persistent_group = 2; + else if (persistent_group) + cfg->persistent_group = 1; + os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + cfg->max_clients = wpa_s->max_stations; + else + cfg->max_clients = wpa_s->conf->max_num_sta; + cfg->cb_ctx = wpa_s; + cfg->ie_update = wpas_p2p_ie_update; + cfg->idle_update = wpas_p2p_idle_update; + + group = p2p_group_init(wpa_s->global->p2p, cfg); + if (group == NULL) + os_free(cfg); + if (!group_formation) + p2p_group_notif_formation_done(group); + wpa_s->p2p_group = group; + return group; +} + + +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!wpa_s->p2p_in_provisioning) { + wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P " + "provisioning not in progress"); + return; + } + + if (ssid && ssid->mode == WPAS_MODE_INFRA) { + u8 go_dev_addr[ETH_ALEN]; + os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN); + wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, + ssid->ssid_len); + /* Clear any stored provisioning info */ + p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr); + } + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + NULL); + if (wpa_s->global->p2p) + p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); + else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + wpa_drv_wps_success_cb(wpa_s, peer_addr); + wpas_group_formation_completed(wpa_s, 1); +} + + +void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ + if (!wpa_s->p2p_in_provisioning) { + wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P " + "provisioning not in progress"); + return; + } + + if (wpa_s->go_params) { + p2p_clear_provisioning_info( + wpa_s->global->p2p, + wpa_s->go_params->peer_device_addr); + } + + wpas_notify_p2p_wps_failed(wpa_s, fail); +} + + +int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *config_method, int join) +{ + u16 config_methods; + + if (os_strncmp(config_method, "display", 7) == 0) + config_methods = WPS_CONFIG_DISPLAY; + else if (os_strncmp(config_method, "keypad", 6) == 0) + config_methods = WPS_CONFIG_KEYPAD; + else if (os_strncmp(config_method, "pbc", 3) == 0 || + os_strncmp(config_method, "pushbutton", 10) == 0) + config_methods = WPS_CONFIG_PUSHBUTTON; + else { + wpa_printf(MSG_DEBUG, "P2P: Unknown config method"); + return -1; + } + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr, + config_methods, join); + } + + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return -1; + + return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, + config_methods, join, 0); +} + + +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + return p2p_scan_result_text(ies, ies_len, buf, end); +} + + +static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->pending_action_tx) + return; + + wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " + "operation request"); + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; +} + + +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id) +{ + wpas_p2p_clear_pending_action_tx(wpa_s); + wpa_s->p2p_long_listen = 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_find(wpa_s, timeout, type); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpa_supplicant_cancel_sched_scan(wpa_s); + + return p2p_find(wpa_s->global->p2p, timeout, type, + num_req_dev_types, req_dev_types, dev_id); +} + + +void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_clear_pending_action_tx(wpa_s); + wpa_s->p2p_long_listen = 0; + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + wpa_s->p2p_cb_on_scan_complete = 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + wpa_drv_p2p_stop_find(wpa_s); + return; + } + + if (wpa_s->global->p2p) + p2p_stop_find(wpa_s->global->p2p); + + wpas_p2p_remove_pending_group_interface(wpa_s); +} + + +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_s->p2p_long_listen = 0; +} + + +int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout) +{ + int res; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpa_supplicant_cancel_sched_scan(wpa_s); + wpas_p2p_clear_pending_action_tx(wpa_s); + + if (timeout == 0) { + /* + * This is a request for unlimited Listen state. However, at + * least for now, this is mapped to a Listen state for one + * hour. + */ + timeout = 3600; + } + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + wpa_s->p2p_long_listen = 0; + + /* + * Stop previous find/listen operation to avoid trying to request a new + * remain-on-channel operation while the driver is still running the + * previous one. + */ + if (wpa_s->global->p2p) + p2p_stop_find(wpa_s->global->p2p); + + res = wpas_p2p_listen_start(wpa_s, timeout * 1000); + if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) { + wpa_s->p2p_long_listen = timeout * 1000; + eloop_register_timeout(timeout, 0, + wpas_p2p_long_listen_timeout, + wpa_s, NULL); + } + + return res; +} + + +int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + u8 *buf, size_t len, int p2p_group) +{ + struct wpabuf *p2p_ie; + int ret; + + if (wpa_s->global->p2p_disabled) + return -1; + if (wpa_s->global->p2p == NULL) + return -1; + if (bss == NULL) + return -1; + + p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len, + p2p_group, p2p_ie); + wpabuf_free(p2p_ie); + + return ret; +} + + +int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled) + return 0; + if (wpa_s->global->p2p == NULL) + return 0; + + return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, + ie, ie_len); +} + + +void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *bssid, + u8 category, const u8 *data, size_t len, int freq) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len, + freq); +} + + +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_scan_ie(wpa_s->global->p2p, ies, NULL); +} + + +void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s) +{ + p2p_group_deinit(wpa_s->p2p_group); + wpa_s->p2p_group = NULL; + + wpa_s->ap_configured_cb = NULL; + wpa_s->ap_configured_cb_ctx = NULL; + wpa_s->ap_configured_cb_data = NULL; + wpa_s->connect_without_scan = NULL; +} + + +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + wpa_s->p2p_long_listen = 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_reject(wpa_s, addr); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return p2p_reject(wpa_s->global->p2p, addr); +} + + +/* Invite to reinvoke a persistent group */ +int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + struct wpa_ssid *ssid, const u8 *go_dev_addr) +{ + enum p2p_invite_role role; + u8 *bssid = NULL; + + if (ssid->mode == WPAS_MODE_P2P_GO) { + role = P2P_INVITE_ROLE_GO; + if (peer_addr == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Missing peer " + "address in invitation command"); + return -1; + } + if (wpas_p2p_create_iface(wpa_s)) { + if (wpas_p2p_add_group_interface(wpa_s, + WPA_IF_P2P_GO) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to " + "allocate a new interface for the " + "group"); + return -1; + } + bssid = wpa_s->pending_interface_addr; + } else + bssid = wpa_s->own_addr; + } else { + role = P2P_INVITE_ROLE_CLIENT; + peer_addr = ssid->bssid; + } + wpa_s->pending_invite_ssid_id = ssid->id; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, + go_dev_addr, 1); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1); +} + + +/* Invite to join an active group */ +int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, + const u8 *peer_addr, const u8 *go_dev_addr) +{ + struct wpa_global *global = wpa_s->global; + enum p2p_invite_role role; + u8 *bssid = NULL; + struct wpa_ssid *ssid; + int persistent; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname); + return -1; + } + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for " + "invitation"); + return -1; + } + + persistent = ssid->p2p_persistent_group && + wpas_p2p_get_persistent(wpa_s->parent, peer_addr, + ssid->ssid, ssid->ssid_len); + + if (ssid->mode == WPAS_MODE_P2P_GO) { + role = P2P_INVITE_ROLE_ACTIVE_GO; + bssid = wpa_s->own_addr; + if (go_dev_addr == NULL) + go_dev_addr = wpa_s->global->p2p_dev_addr; + } else { + role = P2P_INVITE_ROLE_CLIENT; + if (wpa_s->wpa_state < WPA_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot " + "invite to current group"); + return -1; + } + bssid = wpa_s->bssid; + if (go_dev_addr == NULL && + !is_zero_ether_addr(wpa_s->go_dev_addr)) + go_dev_addr = wpa_s->go_dev_addr; + } + wpa_s->parent->pending_invite_ssid_id = -1; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, + go_dev_addr, persistent); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq, + go_dev_addr, persistent); +} + + +void wpas_p2p_completed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + const char *ssid_txt; + u8 go_dev_addr[ETH_ALEN]; + int network_id = -1; + int persistent; + int freq; + + if (!wpa_s->show_group_started || !ssid) + return; + + wpa_s->show_group_started = 0; + + ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len); + os_memset(go_dev_addr, 0, ETH_ALEN); + if (ssid->bssid_set) + os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN); + persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, + ssid->ssid_len); + os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN); + + if (wpa_s->global->p2p_group_formation == wpa_s) + wpa_s->global->p2p_group_formation = NULL; + + freq = wpa_s->current_bss ? wpa_s->current_bss->freq : + (int) wpa_s->assoc_freq; + if (ssid->passphrase == NULL && ssid->psk_set) { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s client ssid=\"%s\" freq=%d psk=%s go_dev_addr=" + MACSTR "%s", + wpa_s->ifname, ssid_txt, freq, psk, + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + } else { + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s client ssid=\"%s\" freq=%d passphrase=\"%s\" " + "go_dev_addr=" MACSTR "%s", + wpa_s->ifname, ssid_txt, freq, + ssid->passphrase ? ssid->passphrase : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); + } + + if (persistent) + network_id = wpas_p2p_store_persistent_group(wpa_s->parent, + ssid, go_dev_addr); + if (network_id < 0) + network_id = ssid->id; + wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1); +} + + +int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, + u32 interval1, u32 duration2, u32 interval2) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || + wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_INFRA) + return -1; + + return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid, + wpa_s->own_addr, wpa_s->assoc_freq, + duration1, interval1, duration2, interval2); +} + + +int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, + unsigned int interval) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return p2p_ext_listen(wpa_s->global->p2p, period, interval); +} + + +static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s) +{ + return wpa_s->current_ssid != NULL && + wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA; +} + + +static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) { + wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - " + "disabled"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " + "group"); + wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT; + wpas_p2p_group_delete(wpa_s); +} + + +static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) +{ + unsigned int timeout; + + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) + return; + + timeout = wpa_s->conf->p2p_group_idle; + if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE)) + timeout = P2P_MAX_CLIENT_IDLE; + + if (timeout == 0) + return; + + if (wpa_s->p2p_in_provisioning) { + /* + * Use the normal group formation timeout during the + * provisioning phase to avoid terminating this process too + * early due to group idle timeout. + */ + wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " + "during provisioning"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", + timeout); + eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout, + wpa_s, NULL); +} + + +void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); +} + + +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); +} + + +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (p2p == NULL) + return; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + return; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME) + p2p_set_dev_name(p2p, wpa_s->conf->device_name); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type); + + if (wpa_s->wps && + (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS)) + p2p_set_config_methods(p2p, wpa_s->wps->config_methods); + + if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)) + p2p_set_uuid(p2p, wpa_s->wps->uuid); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) { + p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer); + p2p_set_model_name(p2p, wpa_s->conf->model_name); + p2p_set_model_number(p2p, wpa_s->conf->model_number); + p2p_set_serial_number(p2p, wpa_s->conf->serial_number); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) + p2p_set_sec_dev_types(p2p, + (void *) wpa_s->conf->sec_device_type, + wpa_s->conf->num_sec_device_types); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) { + int i; + p2p_remove_wps_vendor_extensions(p2p); + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + p2p, wpa_s->conf->wps_vendor_ext[i]); + } + } + + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = 0x04; + p2p_set_country(p2p, country); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) { + p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix, + wpa_s->conf->p2p_ssid_postfix ? + os_strlen(wpa_s->conf->p2p_ssid_postfix) : + 0); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS) + p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) { + u8 reg_class, channel; + int ret; + unsigned int r; + if (wpa_s->conf->p2p_listen_reg_class && + wpa_s->conf->p2p_listen_channel) { + reg_class = wpa_s->conf->p2p_listen_reg_class; + channel = wpa_s->conf->p2p_listen_channel; + } else { + reg_class = 81; + /* + * Pick one of the social channels randomly as the + * listen channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + channel = 1 + (r % 3) * 5; + } + ret = p2p_set_listen_channel(p2p, reg_class, channel); + if (ret) + wpa_printf(MSG_ERROR, "P2P: Own listen channel update " + "failed: %d", ret); + } + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) { + u8 op_reg_class, op_channel, cfg_op_channel; + int ret = 0; + unsigned int r; + if (wpa_s->conf->p2p_oper_reg_class && + wpa_s->conf->p2p_oper_channel) { + op_reg_class = wpa_s->conf->p2p_oper_reg_class; + op_channel = wpa_s->conf->p2p_oper_channel; + cfg_op_channel = 1; + } else { + op_reg_class = 81; + /* + * Use random operation channel from (1, 6, 11) + *if no other preference is indicated. + */ + os_get_random((u8 *) &r, sizeof(r)); + op_channel = 1 + (r % 3) * 5; + cfg_op_channel = 0; + } + ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel, + cfg_op_channel); + if (ret) + wpa_printf(MSG_ERROR, "P2P: Own oper channel update " + "failed: %d", ret); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) { + if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan, + wpa_s->conf->p2p_pref_chan) < 0) { + wpa_printf(MSG_ERROR, "P2P: Preferred channel list " + "update failed"); + } + } +} + + +int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, + int duration) +{ + if (!wpa_s->ap_iface) + return -1; + return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start, + duration); +} + + +int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + + wpa_s->global->cross_connection = enabled; + p2p_set_cross_connect(wpa_s->global->p2p, enabled); + + if (!enabled) { + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (iface->cross_connect_enabled == 0) + continue; + + iface->cross_connect_enabled = 0; + iface->cross_connect_in_use = 0; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } + } + + return 0; +} + + +static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + if (!uplink->global->cross_connection) + return; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (iface->ap_iface == NULL) + continue; + if (iface->cross_connect_in_use) + continue; + + iface->cross_connect_in_use = 1; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } +} + + +static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (!iface->cross_connect_in_use) + continue; + + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + iface->cross_connect_in_use = 0; + } +} + + +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_INFRA || + wpa_s->cross_connect_disallowed) + wpas_p2p_disable_cross_connect(wpa_s); + else + wpas_p2p_enable_cross_connect(wpa_s); + if (!wpa_s->ap_iface) + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); +} + + +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_disable_cross_connect(wpa_s); + if (!wpa_s->ap_iface && + !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout, + wpa_s, NULL)) + wpas_p2p_set_group_idle_timeout(wpa_s); +} + + +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *iface; + + if (!wpa_s->global->cross_connection) + return; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface == wpa_s) + continue; + if (iface->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) + continue; + if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + continue; + + wpa_s->cross_connect_enabled = 1; + os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname, + sizeof(wpa_s->cross_connect_uplink)); + wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from " + "%s to %s whenever uplink is available", + wpa_s->ifname, wpa_s->cross_connect_uplink); + + if (iface->ap_iface || iface->current_ssid == NULL || + iface->current_ssid->mode != WPAS_MODE_INFRA || + iface->cross_connect_disallowed || + iface->wpa_state != WPA_COMPLETED) + break; + + wpa_s->cross_connect_in_use = 1; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + break; + } +} + + +int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT && + !wpa_s->p2p_in_provisioning) + return 0; /* not P2P client operation */ + + wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC " + "session overlap"); + if (wpa_s != wpa_s->parent) + wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); + + if (wpa_s->global->p2p) + p2p_group_formation_failed(wpa_s->global->p2p); + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + + wpas_group_formation_completed(wpa_s, 0); + return 1; +} + + +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +{ + struct p2p_channels chan; + + if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) + return; + + os_memset(&chan, 0, sizeof(chan)); + if (wpas_p2p_setup_channels(wpa_s, &chan)) { + wpa_printf(MSG_ERROR, "P2P: Failed to update supported " + "channel list"); + return; + } + + p2p_update_channel_list(wpa_s->global->p2p, &chan); +} + + +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) +{ + struct wpa_global *global = wpa_s->global; + int found = 0; + const u8 *peer; + + if (global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation"); + + if (wpa_s->pending_interface_name[0] && + !is_zero_ether_addr(wpa_s->pending_interface_addr)) + found = 1; + + peer = p2p_get_go_neg_peer(global->p2p); + if (peer) { + wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer " + MACSTR, MAC2STR(peer)); + p2p_unauthorize(global->p2p, peer); + found = 1; + } + + wpas_p2p_stop_find(wpa_s); + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s == global->p2p_group_formation && + (wpa_s->p2p_in_provisioning || + wpa_s->parent->pending_interface_type == + WPA_IF_P2P_CLIENT)) { + wpa_printf(MSG_DEBUG, "P2P: Interface %s in group " + "formation found - cancelling", + wpa_s->ifname); + found = 1; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + wpas_p2p_group_delete(wpa_s); + break; + } + } + + if (!found) { + wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found"); + return -1; + } + + return 0; +} + + +void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) + return; + + wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not " + "being available anymore"); + wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE; + wpas_p2p_group_delete(wpa_s); +} + + +void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, + int freq_24, int freq_5, int freq_overall) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) + return; + p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall); +} + + +int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr) +{ + u8 peer[ETH_ALEN]; + struct p2p_data *p2p = wpa_s->global->p2p; + + if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) + return -1; + + if (hwaddr_aton(addr, peer)) + return -1; + + return p2p_unauthorize(p2p, peer); +} + + +/** + * wpas_p2p_disconnect - Disconnect from a P2P Group + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This can be used to disconnect from a group in which the local end is a P2P + * Client or to end a P2P Group in case the local end is the Group Owner. If a + * virtual network interface was created for this group, that interface will be + * removed. Otherwise, only the configured P2P group network will be removed + * from the interface. + */ +int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s) +{ + + if (wpa_s == NULL) + return -1; + + wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED; + wpas_p2p_group_delete(wpa_s); + + return 0; +} + + +int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + + return p2p_in_progress(wpa_s->global->p2p); +} + + +void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (wpa_s->p2p_in_provisioning && ssid->p2p_group && + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL) > 0) { + /** + * Remove the network by scheduling the group formation + * timeout to happen immediately. The teardown code + * needs to be scheduled to run asynch later so that we + * don't delete data from under ourselves unexpectedly. + * Calling wpas_p2p_group_formation_timeout directly + * causes a series of crashes in WPS failure scenarios. + */ + wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to " + "P2P group network getting removed"); + eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } +} + + +struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_ssid *s; + size_t i; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled != 2) + continue; + if (ssid && + (ssid_len != s->ssid_len || + os_memcmp(ssid, s->ssid, ssid_len) != 0)) + continue; + if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0) + return s; /* peer is GO in the persistent group */ + if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL) + continue; + for (i = 0; i < s->num_p2p_clients; i++) { + if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, + addr, ETH_ALEN) == 0) + return s; /* peer is P2P client in persistent + * group */ + } + } + + return NULL; +} + + +void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (addr == NULL) + return; + wpas_p2p_add_persistent_group_client(wpa_s, addr); +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h new file mode 100644 index 0000000..4887823 --- /dev/null +++ b/wpa_supplicant/p2p_supplicant.h @@ -0,0 +1,141 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, 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. + */ + +#ifndef P2P_SUPPLICANT_H +#define P2P_SUPPLICANT_H + +enum p2p_wps_method; +struct p2p_go_neg_results; +enum p2p_send_action_result; +struct p2p_peer_info; + +int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s); +void wpas_p2p_deinit(struct wpa_supplicant *wpa_s); +void wpas_p2p_deinit_global(struct wpa_global *global); +int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *pin, enum p2p_wps_method wps_method, + int persistent_group, int join, int auth, int go_intent, + int freq); +void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration); +void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); +int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, + int freq); +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int addr_allocated, + int freq); +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, + int persistent_group, + int group_formation); +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar); +int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *config_method, int join); +void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, + const u8 *data, size_t data_len, + enum p2p_send_action_result result); +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end); +enum p2p_discovery_type; +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id); +void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s); +int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout); +int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + u8 *buf, size_t len, int p2p_group); +int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len); +void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *bssid, + u8 category, const u8 *data, size_t len, int freq); +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); +void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); +void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); +void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res); +void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id); +void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, const u8 *group_id, + size_t group_id_len); +void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods); +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); +void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs); +void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query); +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req); +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs); +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s); +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s); +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp); +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query); +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + struct wpa_ssid *ssid, const u8 *go_dev_addr); +int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, + const u8 *peer_addr, const u8 *go_dev_addr); +void wpas_p2p_completed(struct wpa_supplicant *wpa_s); +int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, + u32 interval1, u32 duration2, u32 interval2); +int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, + unsigned int interval); +void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len); +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len); +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); +int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, + int duration); +int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled); +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s); +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s); +int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s); +void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s); +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s); +void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s); +void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, + int freq_24, int freq_5, int freq_overall); +int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr); +int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s); +void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); +int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s); +void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *ssid, + size_t ssid_len); +void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *addr); + +#endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index edc8c83..afa3be4 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -20,8 +20,9 @@ #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" -#include "mlme.h" #include "wps_supplicant.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" #include "notify.h" #include "bss.h" #include "scan.h" @@ -42,21 +43,21 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) wpas_notify_network_changed(wpa_s); } wpa_supplicant_initiate_eapol(wpa_s); - wpa_printf(MSG_DEBUG, "Already associated with a configured network - " - "generating associated event"); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " + "network - generating associated event"); os_memset(&data, 0, sizeof(data)); wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); } #ifdef CONFIG_WPS -static int wpas_wps_in_use(struct wpa_config *conf, +static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, enum wps_request_type *req_type) { struct wpa_ssid *ssid; int wps = 0; - for (ssid = conf->ssid; ssid; ssid = ssid->next) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) continue; @@ -69,6 +70,16 @@ static int wpas_wps_in_use(struct wpa_config *conf, return 2; } +#ifdef CONFIG_P2P + if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p) { + wpa_s->wps->dev.p2p = 1; + if (!wps) { + wps = 1; + *req_type = WPS_REQ_ENROLLEE_INFO; + } + } +#endif /* CONFIG_P2P */ + return wps; } #endif /* CONFIG_WPS */ @@ -77,12 +88,13 @@ static int wpas_wps_in_use(struct wpa_config *conf, int wpa_supplicant_enabled_networks(struct wpa_config *conf) { struct wpa_ssid *ssid = conf->ssid; + int count = 0; while (ssid) { if (!ssid->disabled) - return 1; + count++; ssid = ssid->next; } - return 0; + return count; } @@ -97,8 +109,8 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, /* ap_scan=2 mode - try to associate with each SSID. */ if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached " - "end of scan list - go back to beginning"); + wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached " + "end of scan list - go back to beginning"); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; wpa_supplicant_req_scan(wpa_s, 0, 0); return; @@ -189,11 +201,7 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, wpa_supplicant_notify_scanning(wpa_s, 1); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - ret = ieee80211_sta_req_scan(wpa_s, params); - else - ret = wpa_drv_scan(wpa_s, params); - + ret = wpa_drv_scan(wpa_s, params); if (ret) { wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); @@ -204,6 +212,63 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } +static void +wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan"); + + if (wpa_supplicant_req_sched_scan(wpa_s)) + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void +wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it"); + + wpa_s->sched_scan_timed_out = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); +} + + +static int +wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval) +{ + int ret; + + wpa_supplicant_notify_scanning(wpa_s, 1); + ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000); + if (ret) + wpa_supplicant_notify_scanning(wpa_s, 0); + else + wpa_s->sched_scanning = 1; + + return ret; +} + + +static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +{ + int ret; + + ret = wpa_drv_stop_sched_scan(wpa_s); + if (ret) { + wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!"); + /* TODO: what to do if stopping fails? */ + return -1; + } + + return ret; +} + + static struct wpa_driver_scan_filter * wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) { @@ -237,20 +302,137 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) } -static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +static void wpa_supplicant_optimize_freqs( + struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { - struct wpa_supplicant *wpa_s = eloop_ctx; - struct wpa_ssid *ssid; - int scan_req = 0, ret; - struct wpabuf *wps_ie = NULL; +#ifdef CONFIG_P2P + if (params->freqs == NULL && wpa_s->p2p_in_provisioning && + wpa_s->go_params) { + /* Optimize provisioning state scan based on GO information */ + if (wpa_s->p2p_in_provisioning < 5 && + wpa_s->go_params->freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO " + "preferred frequency %d MHz", + wpa_s->go_params->freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->go_params->freq; + } else if (wpa_s->p2p_in_provisioning < 8 && + wpa_s->go_params->freq_list[0]) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common " + "channels"); + int_array_concat(¶ms->freqs, + wpa_s->go_params->freq_list); + if (params->freqs) + int_array_sort_unique(params->freqs); + } + wpa_s->p2p_in_provisioning++; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS + if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { + /* + * Optimize post-provisioning scan based on channel used + * during provisioning. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz " + "that was used during provisioning", wpa_s->wps_freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->wps_freq; + wpa_s->after_wps--; + } + +#endif /* CONFIG_WPS */ +} + + +#ifdef CONFIG_INTERWORKING +static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, + struct wpabuf *buf) +{ + if (wpa_s->conf->interworking == 0) + return; + + wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); + wpabuf_put_u8(buf, 4); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x00); + wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */ + + wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); + wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : + 1 + ETH_ALEN); + wpabuf_put_u8(buf, wpa_s->conf->access_network_type); + /* No Venue Info */ + if (!is_zero_ether_addr(wpa_s->conf->hessid)) + wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN); +} +#endif /* CONFIG_INTERWORKING */ + + +static struct wpabuf * +wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + struct wpabuf *extra_ie = NULL; #ifdef CONFIG_WPS int wps = 0; enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking && + wpabuf_resize(&extra_ie, 100) == 0) + wpas_add_interworking_elements(wpa_s, extra_ie); +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_WPS + wps = wpas_wps_in_use(wpa_s, &req_type); + + if (wps) { + struct wpabuf *wps_ie; + wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, + wpa_s->wps->uuid, req_type, + 0, NULL); + if (wps_ie) { + if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) + wpabuf_put_buf(extra_ie, wps_ie); + wpabuf_free(wps_ie); + } + } + +#ifdef CONFIG_P2P + if (wps) { + size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + if (wpabuf_resize(&extra_ie, ielen) == 0) + wpas_p2p_scan_ie(wpa_s, extra_ie); + } +#endif /* CONFIG_P2P */ + +#endif /* CONFIG_WPS */ + + return extra_ie; +} + + +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + int scan_req = 0, ret; + struct wpabuf *extra_ie; struct wpa_driver_scan_params params; size_t max_ssids; enum wpa_states prev_state; + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); + return; + } + if (wpa_s->disconnected && !wpa_s->scan_req) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; @@ -258,15 +440,15 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (!wpa_supplicant_enabled_networks(wpa_s->conf) && !wpa_s->scan_req) { - wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; } if (wpa_s->conf->ap_scan != 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) { - wpa_printf(MSG_DEBUG, "Using wired authentication - " - "overriding ap_scan configuration"); + wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - " + "overriding ap_scan configuration"); wpa_s->conf->ap_scan = 0; wpas_notify_ap_scan_changed(wpa_s); } @@ -276,8 +458,21 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) || - wpa_s->conf->ap_scan == 2) +#ifdef CONFIG_P2P + if (wpas_p2p_in_progress(wpa_s)) { + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " + "while P2P operation is in progress"); + wpa_supplicant_req_scan(wpa_s, 5, 0); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while " + "P2P operation is in progress"); + } + return; + } +#endif /* CONFIG_P2P */ + + if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { max_ssids = wpa_s->max_scan_ssids; @@ -285,10 +480,6 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) max_ssids = WPAS_MAX_SCAN_SSIDS; } -#ifdef CONFIG_WPS - wps = wpas_wps_in_use(wpa_s->conf, &req_type); -#endif /* CONFIG_WPS */ - scan_req = wpa_s->scan_req; wpa_s->scan_req = 0; @@ -299,6 +490,20 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + if (scan_req != 2 && wpa_s->connect_without_scan) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->connect_without_scan) + break; + } + wpa_s->connect_without_scan = NULL; + if (ssid) { + wpa_printf(MSG_DEBUG, "Start a pre-selected network " + "without scan step"); + wpa_supplicant_associate(wpa_s, NULL, ssid); + return; + } + } + /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { @@ -311,9 +516,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } } - if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 || - wpa_s->connect_without_scan)) { - wpa_s->connect_without_scan = 0; + if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { + wpa_s->connect_without_scan = NULL; wpa_supplicant_assoc_try(wpa_s, ssid); return; } else if (wpa_s->conf->ap_scan == 2) { @@ -365,52 +569,55 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (ssid) { wpa_s->prev_scan_ssid = ssid; if (max_ssids > 1) { - wpa_printf(MSG_DEBUG, "Include wildcard SSID in the " - "scan request"); + wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " + "the scan request"); params.num_ssids++; } - wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)"); + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific " + "SSID(s)"); } else { wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; - wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID"); + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " + "SSID"); } -#ifdef CONFIG_WPS - if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { - /* - * Optimize post-provisioning scan based on channel used - * during provisioning. - */ - wpa_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that " - "was used during provisioning", wpa_s->wps_freq); - params.freqs = os_zalloc(2 * sizeof(int)); - if (params.freqs) - params.freqs[0] = wpa_s->wps_freq; - wpa_s->after_wps--; - } + wpa_supplicant_optimize_freqs(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); - if (wps) { - wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, - wpa_s->wps->uuid, req_type); - if (wps_ie) { - params.extra_ies = wpabuf_head(wps_ie); - params.extra_ies_len = wpabuf_len(wps_ie); - } - } -#endif /* CONFIG_WPS */ + if (params.freqs == NULL && wpa_s->next_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " + "generated frequency list"); + params.freqs = wpa_s->next_scan_freqs; + } else + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } + +#ifdef CONFIG_P2P + if (wpa_s->p2p_in_provisioning) { + /* + * The interface may not yet be in P2P mode, so we have to + * explicitly request P2P probe to disable CCK rates. + */ + params.p2p_probe = 1; + } +#endif /* CONFIG_P2P */ ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); - wpabuf_free(wps_ie); + wpabuf_free(extra_ie); os_free(params.freqs); os_free(params.filter_ssids); if (ret) { - wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan"); if (prev_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, prev_state); wpa_supplicant_req_scan(wpa_s, 1, 0); @@ -445,13 +652,13 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) ssid = ssid->next; } if (ssid) { - wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " + wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " "ensure that specific SSID scans occur"); return; } } - wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", + wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); @@ -459,6 +666,160 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) /** + * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * @sec: Number of seconds after which to scan + * @usec: Number of microseconds after which to scan + * + * This function is used to schedule periodic scans for neighboring + * access points after the specified time. + */ +int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + if (!wpa_s->sched_scan_supported) + return -1; + + eloop_register_timeout(sec, usec, + wpa_supplicant_delayed_sched_scan_timeout, + wpa_s, NULL); + + return 0; +} + + +/** + * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to schedule periodic scans for neighboring + * access points repeating the scan continuously. + */ +int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_scan_params params; + enum wpa_states prev_state; + struct wpa_ssid *ssid; + struct wpabuf *wps_ie = NULL; + int ret; + unsigned int max_sched_scan_ssids; + + if (!wpa_s->sched_scan_supported) + return -1; + + if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) + max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; + else + max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + + if (wpa_s->sched_scanning) + return 0; + + os_memset(¶ms, 0, sizeof(params)); + + /* If we can't allocate space for the filters, we just don't filter */ + params.filter_ssids = os_zalloc(wpa_s->max_match_sets * + sizeof(struct wpa_driver_scan_filter)); + + prev_state = wpa_s->wpa_state; + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_INACTIVE) + wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + + /* Find the starting point from which to continue scanning */ + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_sched_ssid) { + while (ssid) { + if (ssid == wpa_s->prev_sched_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + + if (!ssid || !wpa_s->prev_sched_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); + + wpa_s->sched_scan_interval = 2; + wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; + wpa_s->first_sched_scan = 1; + ssid = wpa_s->conf->ssid; + wpa_s->prev_sched_ssid = ssid; + } + + while (ssid) { + if (ssid->disabled) { + wpa_s->prev_sched_ssid = ssid; + ssid = ssid->next; + continue; + } + + if (params.filter_ssids && ssid->ssid && ssid->ssid_len) { + os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid, + ssid->ssid, ssid->ssid_len); + params.filter_ssids[params.num_filter_ssids].ssid_len = + ssid->ssid_len; + params.num_filter_ssids++; + } + + if (ssid->scan_ssid) { + params.ssids[params.num_ssids].ssid = + ssid->ssid; + params.ssids[params.num_ssids].ssid_len = + ssid->ssid_len; + params.num_ssids++; + if (params.num_ssids >= max_sched_scan_ssids) { + wpa_s->prev_sched_ssid = ssid; + break; + } + } + + if (params.num_filter_ssids >= wpa_s->max_match_sets) + break; + wpa_s->prev_sched_ssid = ssid; + ssid = ssid->next; + } + + if (!params.num_ssids) { + os_free(params.filter_ssids); + return 0; + } + + if (wpa_s->wps) + wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); + + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d timeout %d", + wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, + wpa_s->sched_scan_interval); + wpabuf_free(wps_ie); + os_free(params.filter_ssids); + if (ret) { + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); + if (prev_state != wpa_s->wpa_state) + wpa_supplicant_set_state(wpa_s, prev_state); + return ret; + } + + /* If we have more SSIDs to scan, add a timeout so we scan them too */ + if (ssid || !wpa_s->first_sched_scan) { + wpa_s->sched_scan_timed_out = 0; + eloop_register_timeout(wpa_s->sched_scan_timeout, 0, + wpa_supplicant_sched_scan_timeout, + wpa_s, NULL); + wpa_s->first_sched_scan = 0; + wpa_s->sched_scan_timeout /= 2; + wpa_s->sched_scan_interval *= 2; + } + + return 0; +} + + +/** * wpa_supplicant_cancel_scan - Cancel a scheduled scan request * @wpa_s: Pointer to wpa_supplicant data * @@ -467,11 +828,28 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) */ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) { - wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request"); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); } +/** + * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to stop a periodic scheduled scan. + */ +void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->sched_scanning) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan"); + eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL); + wpa_supplicant_stop_sched_scan(wpa_s); +} + + void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning) { @@ -575,15 +953,62 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, } +struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( + const struct wpa_scan_res *res, u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + if (res->beacon_ie_len == 0) + return NULL; + buf = wpabuf_alloc(res->beacon_ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (res + 1); + pos += res->ie_len; + end = pos + res->beacon_ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +/* + * Channels with a great SNR can operate at full rate. What is a great SNR? + * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general + * rule of thumb is that any SNR above 20 is good." This one + * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23 + * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a + * conservative value. + */ +#define GREAT_SNR 30 + /* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { +#define IS_5GHZ(n) (n > 4000) +#define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b, maxrate_a, maxrate_b; + int snr_a, snr_b; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || @@ -604,17 +1029,78 @@ static int wpa_scan_result_compar(const void *a, const void *b) (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; - /* best/max rate preferred if signal level close enough XXX */ - if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || + if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) && + !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) { + snr_a = MIN(wa->level - wa->noise, GREAT_SNR); + snr_b = MIN(wb->level - wb->noise, GREAT_SNR); + } else { + /* Not suitable information to calculate SNR, so use level */ + snr_a = wa->level; + snr_b = wb->level; + } + + /* best/max rate preferred if SNR close enough */ + if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); if (maxrate_a != maxrate_b) return maxrate_b - maxrate_a; + if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) + return IS_5GHZ(wa->freq) ? -1 : 1; } /* use freq for channel preference */ + /* all things being equal, use SNR; if SNRs are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (snr_b == snr_a) + return wb->qual - wa->qual; + return snr_b - snr_a; +#undef MIN +#undef IS_5GHZ +} + + +#ifdef CONFIG_WPS +/* Compare function for sorting scan results when searching a WPS AP for + * provisioning. Return >0 if @b is considered better. */ +static int wpa_scan_result_wps_compar(const void *a, const void *b) +{ + struct wpa_scan_res **_wa = (void *) a; + struct wpa_scan_res **_wb = (void *) b; + struct wpa_scan_res *wa = *_wa; + struct wpa_scan_res *wb = *_wb; + int uses_wps_a, uses_wps_b; + struct wpabuf *wps_a, *wps_b; + int res; + + /* Optimization - check WPS IE existence before allocated memory and + * doing full reassembly. */ + uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL; + uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL; + if (uses_wps_a && !uses_wps_b) + return -1; + if (!uses_wps_a && uses_wps_b) + return 1; + + if (uses_wps_a && uses_wps_b) { + wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE); + wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE); + res = wps_ap_priority_compar(wps_a, wps_b); + wpabuf_free(wps_a); + wpabuf_free(wps_b); + if (res) + return res; + } + + /* + * Do not use current AP security policy as a sorting criteria during + * WPS provisioning step since the AP may get reconfigured at the + * completion of provisioning. + */ + /* all things being equal, use signal level; if signal levels are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ @@ -622,6 +1108,36 @@ static int wpa_scan_result_compar(const void *a, const void *b) return wb->qual - wa->qual; return wb->level - wa->level; } +#endif /* CONFIG_WPS */ + + +static void dump_scan_res(struct wpa_scan_results *scan_res) +{ + size_t i; + + if (scan_res->res == NULL || scan_res->num == 0) + return; + + wpa_printf(MSG_EXCESSIVE, "Sorted scan results"); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *r = scan_res->res[i]; + if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID)) + == WPA_SCAN_LEVEL_DBM) { + int snr = r->level - r->noise; + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " + "noise=%d level=%d snr=%d%s flags=0x%x", + MAC2STR(r->bssid), r->freq, r->qual, + r->noise, r->level, snr, + snr >= GREAT_SNR ? "*" : "", r->flags); + } else { + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " + "noise=%d level=%d flags=0x%x", + MAC2STR(r->bssid), r->freq, r->qual, + r->noise, r->level, r->flags); + } + } +} /** @@ -641,18 +1157,25 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, { struct wpa_scan_results *scan_res; size_t i; + int (*compar)(const void *, const void *) = wpa_scan_result_compar; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - scan_res = ieee80211_sta_get_scan_results(wpa_s); - else - scan_res = wpa_drv_get_scan_results2(wpa_s); + scan_res = wpa_drv_get_scan_results2(wpa_s); if (scan_res == NULL) { - wpa_printf(MSG_DEBUG, "Failed to get scan results"); + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); return NULL; } +#ifdef CONFIG_WPS + if (wpas_wps_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " + "provisioning rules"); + compar = wpa_scan_result_wps_compar; + } +#endif /* CONFIG_WPS */ + qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); + compar); + dump_scan_res(scan_res); wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) @@ -673,17 +1196,3 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) return 0; } - - -void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index 441fdbb..7fb84e6 100644 --- a/wpa_supplicant/scan.h +++ b/wpa_supplicant/scan.h @@ -17,7 +17,11 @@ int wpa_supplicant_enabled_networks(struct wpa_config *conf); void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); +int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, + int sec, int usec); +int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); +void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning); struct wpa_driver_scan_params; @@ -32,6 +36,7 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type); struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, u32 vendor_type); -void wpa_scan_results_free(struct wpa_scan_results *res); +struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( + const struct wpa_scan_res *res, u32 vendor_type); #endif /* SCAN_H */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 5604e97..4c17ef3 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" @@ -26,12 +27,23 @@ #include "driver_i.h" #include "wpas_glue.h" #include "wps_supplicant.h" +#include "p2p_supplicant.h" #include "notify.h" #include "blacklist.h" #include "bss.h" #include "scan.h" #include "sme.h" +#define SME_AUTH_TIMEOUT 5 +#define SME_ASSOC_TIMEOUT 5 + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_IEEE80211W */ + + void sme_authenticate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { @@ -46,8 +58,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, int i, bssid_changed; if (bss == NULL) { - wpa_printf(MSG_ERROR, "SME: No scan result available for the " - "network"); + wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " + "the network"); return; } @@ -60,6 +72,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, params.bssid = bss->bssid; params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; + params.p2p = ssid->p2p_group; if (wpa_s->sme.ssid_len != params.ssid_len || os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0) @@ -80,12 +93,12 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, } } #endif /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", - params.auth_alg); + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", + params.auth_alg); if (ssid->auth_alg) { params.auth_alg = ssid->auth_alg; - wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", - params.auth_alg); + wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " + "0x%x", params.auth_alg); } for (i = 0; i < NUM_WEP_KEYS; i++) { @@ -119,8 +132,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { - wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " - "management and encryption suites"); + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites"); return; } } else if (ssid->key_mgmt & @@ -132,9 +145,9 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { - wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " - "management and encryption suites (no scan " - "results)"); + wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " + "key management and encryption suites (no " + "scan results)"); return; } #ifdef CONFIG_WPS @@ -185,8 +198,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (wpa_s->sme.ft_used && os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && wpa_sm_has_ptk(wpa_s->wpa)) { - wpa_printf(MSG_DEBUG, "SME: Trying to use FT " - "over-the-air"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " + "over-the-air"); params.auth_alg = WPA_AUTH_ALG_FT; params.ie = wpa_s->sme.ft_ies; params.ie_len = wpa_s->sme.ft_ies_len; @@ -202,16 +215,50 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && _ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { - wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " - "require MFP"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports " + "MFP: require MFP"); wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; } } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + u8 *pos; + size_t len; + int res; + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, + ssid->p2p_group); + if (res >= 0) + wpa_s->sme.assoc_req_ie_len += res; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking) { + u8 *pos = wpa_s->sme.assoc_req_ie; + if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + 6, pos, + wpa_s->sme.assoc_req_ie_len - + (pos - wpa_s->sme.assoc_req_ie)); + wpa_s->sme.assoc_req_ie_len += 6; + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = 4; + *pos++ = 0x00; + *pos++ = 0x00; + *pos++ = 0x00; + *pos++ = 0x80; /* Bit 31 - Interworking */ + } +#endif /* CONFIG_INTERWORKING */ + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); - wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR + wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); @@ -226,13 +273,15 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_s->sme.auth_alg = params.auth_alg; if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { - wpa_msg(wpa_s, MSG_INFO, "Authentication request to the " + wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the " "driver failed"); - wpa_supplicant_req_scan(wpa_s, 1, 0); + wpas_connection_failed(wpa_s, bss->bssid); + wpa_supplicant_mark_disassoc(wpa_s); return; } - /* TODO: add timeout on authentication */ + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); /* * Association will be started based on the authentication event from @@ -246,46 +295,50 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " - "network is not selected"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when network is not selected"); return; } if (wpa_s->wpa_state != WPA_AUTHENTICATING) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " - "not in authenticating state"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event " + "when not in authenticating state"); return; } if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "SME: Ignore authentication with " - "unexpected peer " MACSTR, - MAC2STR(data->auth.peer)); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with " + "unexpected peer " MACSTR, + MAC2STR(data->auth.peer)); return; } - wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR - " auth_type=%d status_code=%d", - MAC2STR(data->auth.peer), data->auth.auth_type, - data->auth.status_code); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR + " auth_type=%d status_code=%d", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.status_code); wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + if (data->auth.status_code != WLAN_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, "SME: Authentication failed (status " - "code %d)", data->auth.status_code); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " + "code %d)", data->auth.status_code); if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || wpa_s->sme.auth_alg == data->auth.auth_type || - wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) + wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); return; + } switch (data->auth.auth_type) { case WLAN_AUTH_OPEN: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; - wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; @@ -293,7 +346,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) case WLAN_AUTH_SHARED_KEY: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP; - wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth"); wpa_supplicant_associate(wpa_s, wpa_s->current_bss, wpa_s->current_ssid); return; @@ -333,6 +386,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.group_suite = cipher_suite2driver(wpa_s->group_cipher); #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { params.wpa_ie = wpa_s->sme.ft_ies; @@ -354,26 +409,37 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, if (params.wpa_ie == NULL || ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0) < 0) { - wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!"); os_memset(&elems, 0, sizeof(elems)); } - if (elems.rsn_ie) + if (elems.rsn_ie) { + params.wpa_proto = WPA_PROTO_RSN; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2, elems.rsn_ie_len + 2); - else if (elems.wpa_ie) + } else if (elems.wpa_ie) { + params.wpa_proto = WPA_PROTO_WPA; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2, elems.wpa_ie_len + 2); - else + } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) + params.p2p = 1; + + if (wpa_s->parent->set_sta_uapsd) + params.uapsd = wpa_s->parent->sta_uapsd; + else + params.uapsd = -1; if (wpa_drv_associate(wpa_s, ¶ms) < 0) { - wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " - "failed"); - wpa_supplicant_req_scan(wpa_s, 5, 0); + wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the " + "driver failed"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } - /* TODO: add timeout on association */ + eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s, + NULL); } @@ -381,7 +447,7 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, const u8 *ies, size_t ies_len) { if (md == NULL || ies == NULL) { - wpa_printf(MSG_DEBUG, "SME: Remove mobility domain"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain"); os_free(wpa_s->sme.ft_ies); wpa_s->sme.ft_ies = NULL; wpa_s->sme.ft_ies_len = 0; @@ -401,90 +467,303 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, } -void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static void sme_deauth(struct wpa_supplicant *wpa_s) { int bssid_changed; - int timeout = 5000; - - wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: " - "status code %d", MAC2STR(wpa_s->pending_bssid), - data->assoc_reject.status_code); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); - /* - * For now, unconditionally terminate the previous authentication. In - * theory, this should not be needed, but mac80211 gets quite confused - * if the authentication is left pending.. Some roaming cases might - * benefit from using the previous authentication, so this could be - * optimized in the future. - */ if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, WLAN_REASON_DEAUTH_LEAVING) < 0) { - wpa_msg(wpa_s, MSG_INFO, - "Deauth request to the driver failed"); + wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver " + "failed"); } wpa_s->sme.prev_bssid_set = 0; - if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) { - struct wpa_blacklist *b; - b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid); - if (b && b->count < 3) { - /* - * Speed up next attempt if there could be other APs - * that could accept association. - */ - timeout = 100; - } - } + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); if (bssid_changed) wpas_notify_bssid_changed(wpa_s); +} + + +void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: " + "status code %d", MAC2STR(wpa_s->pending_bssid), + data->assoc_reject.status_code); + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); /* - * TODO: if more than one possible AP is available in scan results, - * could try the other ones before requesting a new scan. + * For now, unconditionally terminate the previous authentication. In + * theory, this should not be needed, but mac80211 gets quite confused + * if the authentication is left pending.. Some roaming cases might + * benefit from using the previous authentication, so this could be + * optimized in the future. */ - wpa_supplicant_req_scan(wpa_s, timeout / 1000, - 1000 * (timeout % 1000)); + sme_deauth(wpa_s); } void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Authentication timed out"); - wpa_supplicant_req_scan(wpa_s, 5, 0); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); } void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Association timed out"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out"); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_mark_disassoc(wpa_s); - wpa_supplicant_req_scan(wpa_s, 5, 0); } void sme_event_disassoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - wpa_printf(MSG_DEBUG, "SME: Disassociation event received"); - if (wpa_s->sme.prev_bssid_set && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); + if (wpa_s->sme.prev_bssid_set) { /* * cfg80211/mac80211 can get into somewhat confused state if * the AP only disassociates us and leaves us in authenticated * state. For now, force the state to be cleared to avoid * confusing errors if we try to associate with the AP again. */ - wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver " - "state"); + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear " + "driver state"); wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid, WLAN_REASON_DEAUTH_LEAVING); } } + + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_AUTHENTICATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout"); + sme_deauth(wpa_s); + } +} + + +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->wpa_state == WPA_ASSOCIATING) { + wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout"); + sme_deauth(wpa_s); + } +} + + +void sme_state_changed(struct wpa_supplicant *wpa_s) +{ + /* Make sure timers are cleaned up appropriately. */ + if (wpa_s->wpa_state != WPA_ASSOCIATING) + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + if (wpa_s->wpa_state != WPA_AUTHENTICATING) + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +} + + +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid) +{ + /* + * mac80211-workaround to force deauth on failed auth cmd, + * requires us to remain in authenticating state to allow the + * second authentication attempt to be continued properly. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication " + "to proceed after disconnection event"); + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN); + + /* + * Re-arm authentication timer in case auth fails for whatever reason. + */ + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s, + NULL); +} + + +void sme_deinit(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->sme.ft_ies); + wpa_s->sme.ft_ies = NULL; + wpa_s->sme.ft_ies_len = 0; +#ifdef CONFIG_IEEE80211W + sme_stop_sa_query(wpa_s); +#endif /* CONFIG_IEEE80211W */ + + eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +} + + +#ifdef CONFIG_IEEE80211W + +static const unsigned int sa_query_max_timeout = 1000; +static const unsigned int sa_query_retry_timeout = 201; + +static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (sa_query_max_timeout < tu) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out"); + sme_stop_sa_query(wpa_s); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID); + return 1; + } + + return 0; +} + + +static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, + const u8 *trans_id) +{ + u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN]; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to " + MACSTR, MAC2STR(wpa_s->bssid)); + wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + req[0] = WLAN_ACTION_SA_QUERY; + req[1] = WLAN_SA_QUERY_REQUEST; + os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + req, sizeof(req), 0) < 0) + wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query " + "Request"); +} + + +static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (wpa_s->sme.sa_query_count > 0 && + sme_check_sa_query_timeout(wpa_s)) + return; + + nbuf = os_realloc(wpa_s->sme.sa_query_trans_id, + (wpa_s->sme.sa_query_count + 1) * + WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (wpa_s->sme.sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&wpa_s->sme.sa_query_start); + } + trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + wpa_s->sme.sa_query_trans_id = nbuf; + wpa_s->sme.sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL); + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d", + wpa_s->sme.sa_query_count); + + sme_send_sa_query_req(wpa_s, trans_id); +} + + +static void sme_start_sa_query(struct wpa_supplicant *wpa_s) +{ + sme_sa_query_timer(wpa_s, NULL); +} + + +void sme_stop_sa_query(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); + os_free(wpa_s->sme.sa_query_trans_id); + wpa_s->sme.sa_query_trans_id = NULL; + wpa_s->sme.sa_query_count = 0; +} + + +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code) +{ + struct wpa_ssid *ssid; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + return; + if (wpa_s->wpa_state != WPA_COMPLETED) + return; + ssid = wpa_s->current_ssid; + if (ssid == NULL || ssid->ieee80211w == 0) + return; + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA && + reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA) + return; + if (wpa_s->sme.sa_query_count > 0) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - " + "possible AP/STA state mismatch - trigger SA Query"); + sme_start_sa_query(wpa_s); +} + + +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len) +{ + int i; + + if (wpa_s->sme.sa_query_trans_id == NULL || + len < 1 + WLAN_SA_QUERY_TR_ID_LEN || + data[0] != WLAN_SA_QUERY_RESPONSE) + return; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from " + MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + + for (i = 0; i < wpa_s->sme.sa_query_count; i++) { + if (os_memcmp(wpa_s->sme.sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= wpa_s->sme.sa_query_count) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query " + "transaction identifier found"); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received " + "from " MACSTR, MAC2STR(sa)); + sme_stop_sa_query(wpa_s); +} + +#endif /* CONFIG_IEEE80211W */ diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index 3ec8cc9..a59b38d 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -32,6 +32,14 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data); void sme_event_disassoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data); +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code); +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len); +void sme_state_changed(struct wpa_supplicant *wpa_s); +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid); +void sme_deinit(struct wpa_supplicant *wpa_s); #else /* CONFIG_SME */ @@ -73,6 +81,26 @@ static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, { } +static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *da, + u16 reason_code) +{ +} + +static inline void sme_state_changed(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, + const u8 *prev_pending_bssid) +{ +} + +static inline void sme_deinit(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff --git a/wpa_supplicant/symbian/wpa_supplicant.mmp b/wpa_supplicant/symbian/wpa_supplicant.mmp index 217908e..e018e05 100644 --- a/wpa_supplicant/symbian/wpa_supplicant.mmp +++ b/wpa_supplicant/symbian/wpa_supplicant.mmp @@ -15,7 +15,7 @@ SOURCE wpa_supplicant.c events.c SOURCEPATH ..\..\src\rsn_supp SOURCE wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c SOURCEPATH ..\..\src\drivers -SOURCE drivers.c +SOURCE drivers.c driver_common.c SOURCEPATH ..\..\src\common SOURCE wpa_common.c SOURCEPATH ..\..\src\utils diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in new file mode 100644 index 0000000..76aba12 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface- and nl80211 driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-nl80211@wlan0.service diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in b/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in new file mode 100644 index 0000000..ff384ae --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface- and wired driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-wired@wlan0.service diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in new file mode 100644 index 0000000..4351ad8 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=WPA supplicant + +[Service] +Type=dbus +BusName=fi.epitest.hostap.WPASupplicant +ExecStart=@BINDIR@/wpa_supplicant -u + +[Install] +WantedBy=multi-user.target +Alias=dbus-fi.epitest.hostap.WPASupplicant.service diff --git a/wpa_supplicant/systemd/wpa_supplicant@.service.in b/wpa_supplicant/systemd/wpa_supplicant@.service.in new file mode 100644 index 0000000..c215567 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant@.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant@wlan0.service diff --git a/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj new file mode 100644 index 0000000..38b29c4 --- /dev/null +++ b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj @@ -0,0 +1,469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj new file mode 100644 index 0000000..d2de768 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj new file mode 100644 index 0000000..b107842 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj new file mode 100644 index 0000000..e3886b7 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj index 4d402e5..1034891 100755 --- a/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj +++ b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj @@ -251,6 +251,10 @@ > + + diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 162a0b8..7da13c3 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * * 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 @@ -19,22 +19,21 @@ #ifdef CONFIG_CTRL_IFACE_UNIX #include #endif /* CONFIG_CTRL_IFACE_UNIX */ -#ifdef CONFIG_READLINE -#include -#include -#endif /* CONFIG_READLINE */ -#ifdef CONFIG_WPA_CLI_FORK -#include -#endif /* CONFIG_WPA_CLI_FORK */ #include "common/wpa_ctrl.h" -#include "common.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/edit.h" +#include "utils/list.h" #include "common/version.h" +#ifdef ANDROID +#include +#endif /* ANDROID */ static const char *wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2010, Jouni Malinen and contributors"; +"Copyright (c) 2004-2012, Jouni Malinen and contributors"; static const char *wpa_cli_license = @@ -91,22 +90,32 @@ static const char *wpa_cli_full_license = static struct wpa_ctrl *ctrl_conn; static struct wpa_ctrl *mon_conn; -#ifdef CONFIG_WPA_CLI_FORK -static pid_t mon_pid = 0; -#endif /* CONFIG_WPA_CLI_FORK */ static int wpa_cli_quit = 0; static int wpa_cli_attached = 0; static int wpa_cli_connected = 0; static int wpa_cli_last_id = 0; -static const char *ctrl_iface_dir = "/var/run/wpa_supplicant"; +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; static char *ctrl_ifname = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; static int ping_interval = 5; static int interactive = 0; +struct cli_txt_entry { + struct dl_list list; + char *txt; +}; + +static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ -static void print_help(); + +static void print_help(void); +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); static void usage(void) @@ -121,63 +130,195 @@ static void usage(void) "events from\n" " wpa_supplicant\n" " -B = run a daemon in the background\n" - " default path: /var/run/wpa_supplicant\n" + " default path: " CONFIG_CTRL_IFACE_DIR "\n" " default interface: first interface found in socket path\n"); print_help(); } -#ifdef CONFIG_WPA_CLI_FORK -static int in_query = 0; +static void cli_txt_list_free(struct cli_txt_entry *e) +{ + dl_list_del(&e->list); + os_free(e->txt); + os_free(e); +} + -static void wpa_cli_monitor_sig(int sig) +static void cli_txt_list_flush(struct dl_list *list) { - if (sig == SIGUSR1) - in_query = 1; - else if (sig == SIGUSR2) - in_query = 0; + struct cli_txt_entry *e; + while ((e = dl_list_first(list, struct cli_txt_entry, list))) + cli_txt_list_free(e); } -static void wpa_cli_monitor(void) + +static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, + const char *txt) { - char buf[256]; - size_t len = sizeof(buf) - 1; - struct timeval tv; - fd_set rfds; + struct cli_txt_entry *e; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + if (os_strcmp(e->txt, txt) == 0) + return e; + } + return NULL; +} - signal(SIGUSR1, wpa_cli_monitor_sig); - signal(SIGUSR2, wpa_cli_monitor_sig); - while (mon_conn) { - int s = wpa_ctrl_get_fd(mon_conn); - tv.tv_sec = 5; - tv.tv_usec = 0; - FD_ZERO(&rfds); - FD_SET(s, &rfds); - if (select(s + 1, &rfds, NULL, NULL, &tv) < 0) { - if (errno == EINTR) - continue; - perror("select"); - break; - } - if (mon_conn == NULL) +static void cli_txt_list_del(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + e = cli_txt_list_get(txt_list, txt); + if (e) + cli_txt_list_free(e); +} + + +static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + if (hwaddr_aton(txt, addr) < 0) + return; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + cli_txt_list_del(txt_list, buf); +} + + +#ifdef CONFIG_P2P +static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt) +{ + const char *end; + char *buf; + end = os_strchr(txt, ' '); + if (end == NULL) + end = txt + os_strlen(txt); + buf = os_malloc(end - txt + 1); + if (buf == NULL) + return; + os_memcpy(buf, txt, end - txt); + buf[end - txt] = '\0'; + cli_txt_list_del(txt_list, buf); + os_free(buf); +} +#endif /* CONFIG_P2P */ + + +static int cli_txt_list_add(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + e = cli_txt_list_get(txt_list, txt); + if (e) + return 0; + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + e->txt = os_strdup(txt); + if (e->txt == NULL) { + os_free(e); + return -1; + } + dl_list_add(txt_list, &e->list); + return 0; +} + + +#ifdef CONFIG_P2P +static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + if (hwaddr_aton(txt, addr) < 0) + return -1; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + return cli_txt_list_add(txt_list, buf); +} + + +static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt) +{ + const char *end; + char *buf; + int ret; + end = os_strchr(txt, ' '); + if (end == NULL) + end = txt + os_strlen(txt); + buf = os_malloc(end - txt + 1); + if (buf == NULL) + return -1; + os_memcpy(buf, txt, end - txt); + buf[end - txt] = '\0'; + ret = cli_txt_list_add(txt_list, buf); + os_free(buf); + return ret; +} +#endif /* CONFIG_P2P */ + + +static char ** cli_txt_list_array(struct dl_list *txt_list) +{ + unsigned int i, count = dl_list_len(txt_list); + char **res; + struct cli_txt_entry *e; + + res = os_zalloc((count + 1) * sizeof(char *)); + if (res == NULL) + return NULL; + + i = 0; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) break; - if (FD_ISSET(s, &rfds)) { - len = sizeof(buf) - 1; - int res = wpa_ctrl_recv(mon_conn, buf, &len); - if (res < 0) { - perror("wpa_ctrl_recv"); - break; - } - buf[len] = '\0'; - if (in_query) - printf("\r"); - printf("%s\n", buf); - kill(getppid(), SIGUSR1); + i++; + } + + return res; +} + + +static int get_cmd_arg_num(const char *str, int pos) +{ + int arg = 0, i; + + for (i = 0; i <= pos; i++) { + if (str[i] != ' ') { + arg++; + while (i <= pos && str[i] != ' ') + i++; } } + + if (arg > 0) + arg--; + return arg; +} + + +static int str_starts(const char *src, const char *match) +{ + return os_strncmp(src, match, os_strlen(match)) == 0; +} + + +static int wpa_cli_show_event(const char *event) +{ + const char *start; + + start = os_strchr(event, '>'); + if (start == NULL) + return 1; + + start++; + /* + * Skip BSS added/removed events since they can be relatively frequent + * and are likely of not much use for an interactive user. + */ + if (str_starts(start, WPA_EVENT_BSS_ADDED) || + str_starts(start, WPA_EVENT_BSS_REMOVED)) + return 0; + + return 1; } -#endif /* CONFIG_WPA_CLI_FORK */ static int wpa_cli_open_connection(const char *ifname, int attach) @@ -192,20 +333,31 @@ static int wpa_cli_open_connection(const char *ifname, int attach) else mon_conn = NULL; #else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */ - char *cfile; + char *cfile = NULL; int flen, res; if (ifname == NULL) return -1; - flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; - cfile = os_malloc(flen); - if (cfile == NULL) - return -1L; - res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - if (res < 0 || res >= flen) { - os_free(cfile); - return -1; +#ifdef ANDROID + if (access(ctrl_iface_dir, F_OK) < 0) { + cfile = os_strdup(ifname); + if (cfile == NULL) + return -1; + } +#endif /* ANDROID */ + + if (cfile == NULL) { + flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2; + cfile = os_malloc(flen); + if (cfile == NULL) + return -1; + res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, + ifname); + if (res < 0 || res >= flen) { + os_free(cfile); + return -1; + } } ctrl_conn = wpa_ctrl_open(cfile); @@ -224,26 +376,15 @@ static int wpa_cli_open_connection(const char *ifname, int attach) if (mon_conn) { if (wpa_ctrl_attach(mon_conn) == 0) { wpa_cli_attached = 1; + if (interactive) + eloop_register_read_sock( + wpa_ctrl_get_fd(mon_conn), + wpa_cli_mon_receive, NULL, NULL); } else { printf("Warning: Failed to attach to " "wpa_supplicant.\n"); return -1; } - -#ifdef CONFIG_WPA_CLI_FORK - { - pid_t p = fork(); - if (p < 0) { - perror("fork"); - return -1; - } - if (p == 0) { - wpa_cli_monitor(); - exit(0); - } else - mon_pid = p; - } -#endif /* CONFIG_WPA_CLI_FORK */ } return 0; @@ -255,15 +396,6 @@ static void wpa_cli_close_connection(void) if (ctrl_conn == NULL) return; -#ifdef CONFIG_WPA_CLI_FORK - if (mon_pid) { - int status; - kill(mon_pid, SIGPIPE); - wait(&status); - mon_pid = 0; - } -#endif /* CONFIG_WPA_CLI_FORK */ - if (wpa_cli_attached) { wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn); wpa_cli_attached = 0; @@ -271,6 +403,7 @@ static void wpa_cli_close_connection(void) wpa_ctrl_close(ctrl_conn); ctrl_conn = NULL; if (mon_conn) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn)); wpa_ctrl_close(mon_conn); mon_conn = NULL; } @@ -306,6 +439,8 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) if (print) { buf[len] = '\0'; printf("%s", buf); + if (interactive && len > 0 && buf[len - 1] != '\n') + printf("\n"); } return 0; } @@ -330,6 +465,25 @@ static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOG"); +} + + +static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int ret; + if (argc == 0) + return -1; + ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]); + if (ret < 0 || (size_t) ret >= sizeof(cmd)) + return -1; + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "MIB"); @@ -359,6 +513,8 @@ static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { wpa_cli_quit = 1; + if (interactive) + eloop_terminate(); return 0; } @@ -393,13 +549,17 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) return 0; } - if (argc != 2) { + if (argc != 1 && argc != 2) { printf("Invalid SET command: needs two arguments (variable " "name and value)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); + if (argc == 1) + res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]); + else + res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", + argv[0], argv[1]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { printf("Too long SET command.\n"); return -1; @@ -408,6 +568,26 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid GET command: need one argument (variable " + "name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long GET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "LOGOFF"); @@ -467,6 +647,66 @@ static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid SCAN_INTERVAL command: needs one argument " + "scan_interval value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SCAN_INTERVAL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid BSS_EXPIRE_AGE command: needs one argument " + "(bss_expire_age value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long BSS_EXPIRE_AGE command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid BSS_EXPIRE_COUNT command: needs one argument " + "(bss_expire_count value)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long BSS_EXPIRE_COUNT command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -561,6 +801,39 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1 && argc != 2) { + printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" + "- PIN to be verified\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_CHECK_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + #ifdef CONFIG_WPS_OOB static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -602,7 +875,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) if (argc == 2) res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]); - else if (argc == 6) { + else if (argc == 5 || argc == 6) { char ssid_hex[2 * 32 + 1]; char key_hex[2 * 64 + 1]; int i; @@ -615,10 +888,13 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) } key_hex[0] = '\0'; - for (i = 0; i < 64; i++) { - if (argv[5][i] == '\0') - break; - os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]); + if (argc == 6) { + for (i = 0; i < 64; i++) { + if (argv[5][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[5][i]); + } } res = os_snprintf(cmd, sizeof(cmd), @@ -627,11 +903,11 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) key_hex); } else { printf("Invalid WPS_REG command: need two arguments:\n" - "- BSSID: use 'any' to select any\n" + "- BSSID of the target AP\n" "- AP PIN\n"); printf("Alternatively, six arguments can be used to " "reconfigure the AP:\n" - "- BSSID: use 'any' to select any\n" + "- BSSID of the target AP\n" "- AP PIN\n" "- new SSID\n" "- new auth (OPEN, WPAPSK, WPA2PSK)\n" @@ -648,11 +924,44 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid WPS_AP_PIN command: needs at least one " + "argument\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_AP_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, char *argv[]) { + char cmd[100]; + if (argc > 0) { + os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); + } return wpa_ctrl_command(ctrl, "WPS_ER_START"); - } @@ -670,15 +979,21 @@ static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, char cmd[256]; int res; - if (argc != 2) { - printf("Invalid WPS_ER_PIN command: need two arguments:\n" + if (argc < 2) { + printf("Invalid WPS_ER_PIN command: need at least two " + "arguments:\n" "- UUID: use 'any' to select any\n" - "- PIN: Enrollee PIN\n"); + "- PIN: Enrollee PIN\n" + "optional: - Enrollee MAC address\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", - argv[0], argv[1]); + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + argv[0], argv[1]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { printf("Too long WPS_ER_PIN command.\n"); return -1; @@ -732,43 +1047,118 @@ static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, } -static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[256]; int res; - if (argc != 1) { - printf("Invalid IBSS_RSN command: needs one argument " - "(Peer STA MAC address)\n"); + if (argc != 2) { + printf("Invalid WPS_ER_SET_CONFIG command: need two " + "arguments:\n" + "- UUID: specify which AP to use\n" + "- Network configuration id\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]); + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s", + argv[0], argv[1]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long IBSS_RSN command.\n"); + printf("Too long WPS_ER_SET_CONFIG command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[256]; int res; - if (argc != 1) { - printf("Invalid LEVEL command: needs one argument (debug " - "level)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long LEVEL command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); -} + if (argc == 5 || argc == 6) { + char ssid_hex[2 * 32 + 1]; + char key_hex[2 * 64 + 1]; + int i; + + ssid_hex[0] = '\0'; + for (i = 0; i < 32; i++) { + if (argv[2][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); + } + + key_hex[0] = '\0'; + if (argc == 6) { + for (i = 0; i < 64; i++) { + if (argv[5][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[5][i]); + } + } + + res = os_snprintf(cmd, sizeof(cmd), + "WPS_ER_CONFIG %s %s %s %s %s %s", + argv[0], argv[1], ssid_hex, argv[3], argv[4], + key_hex); + } else { + printf("Invalid WPS_ER_CONFIG command: need six arguments:\n" + "- AP UUID\n" + "- AP PIN\n" + "- new SSID\n" + "- new auth (OPEN, WPAPSK, WPA2PSK)\n" + "- new encr (NONE, WEP, TKIP, CCMP)\n" + "- new key\n"); + return -1; + } + + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_CONFIG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid IBSS_RSN command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long IBSS_RSN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long LEVEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1002,6 +1392,58 @@ static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "BLACKLIST"); + if (ret < 0 || ret >= end - pos) { + printf("Too long BLACKLIST command.\n"); + return -1; + } + pos += ret; + for (i = 0; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long BLACKLIST command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "LOG_LEVEL"); + if (ret < 0 || ret >= end - pos) { + printf("Too long LOG_LEVEL command.\n"); + return -1; + } + pos += ret; + for (i = 0; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long LOG_LEVEL command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1229,6 +1671,21 @@ static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** wpa_cli_complete_bss(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&bsses); + break; + } + + return res; +} + + static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1393,73 +1850,826 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, return -1; } - buf[len] = '\0'; - if (memcmp(buf, "FAIL", 4) == 0) + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + printf("%s", buf); + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos = '\0'; + os_strlcpy(addr, buf, addr_len); + return 0; +} + + +static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + return 0; + do { + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + + return -1; +} +#endif /* CONFIG_AP */ + + +static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SUSPEND"); +} + + +static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RESUME"); +} + + +static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DROP_SA"); +} + + +static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid ROAM command: needs one argument " + "(target AP's BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long ROAM command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +#ifdef CONFIG_P2P + +static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_FIND"); + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_STOP_FIND"); +} + + +static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc < 2) { + printf("Invalid P2P_CONNECT command: needs at least two " + "arguments (address and pbc/PIN)\n"); + return -1; + } + + if (argc > 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_CONNECT %s %s %s %s %s", + argv[0], argv[1], argv[2], argv[3], + argv[4]); + else if (argc > 3) + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static char ** wpa_cli_complete_p2p_connect(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_peers); + break; + } + + return res; +} + + +static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_LISTEN"); + + res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_GROUP_REMOVE command: needs one argument " + "(interface name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_groups); + break; + } + + return res; +} + + +static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD"); + + if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 2 && argc != 3) { + printf("Invalid P2P_PROV_DISC command: needs at least " + "two arguments, address and config method\n" + "(display, keypad, or pbc) and an optional join\n"); + return -1; + } + + if (argc == 3) + res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE"); +} + + +static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 2 && argc != 4) { + printf("Invalid P2P_SERV_DISC_REQ command: needs two " + "arguments (address and TLVs) or four arguments " + "(address, \"upnp\", version, search target " + "(SSDP ST:)\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERV_DISC_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one " + "argument (pending request identifier)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 4) { + printf("Invalid P2P_SERV_DISC_RESP command: needs four " + "arguments (freq, address, dialog token, and TLVs)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE"); +} + + +static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one " + "argument (external processing: 0/1)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 3 && argc != 4) { + printf("Invalid P2P_SERVICE_ADD command: needs three or four " + "arguments\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_ADD %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_ADD %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 2 && argc != 3) { + printf("Invalid P2P_SERVICE_DEL command: needs two or three " + "arguments\n"); + return -1; + } + + if (argc == 3) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_DEL %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), + "P2P_SERVICE_DEL %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc != 1) { + printf("Invalid P2P_REJECT command: needs one argument " + "(peer address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[128]; + int res; + + if (argc < 1) { + printf("Invalid P2P_INVITE command: needs at least one " + "argument\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'p2p_peer' command - exactly one argument, " + "P2P peer device address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static char ** wpa_cli_complete_p2p_peer(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_peers); + break; + } + + return res; +} + + +static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len, + int discovered) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) + return -1; + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos++ = '\0'; + os_strlcpy(addr, buf, addr_len); + if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL) + printf("%s\n", addr); + return 0; +} + + +static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char addr[32], cmd[64]; + int discovered; + + discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0; + + if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST", + addr, sizeof(addr), discovered)) + return -1; + do { + os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr); + } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr), + discovered) == 0); + + return 0; +} + + +static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 2) { + printf("Invalid P2P_SET command: needs two arguments (field, " + "value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_CANCEL"); +} + + +static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 1) { + printf("Invalid P2P_UNAUTHORIZE command: needs one argument " + "(peer address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]); + + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 0 && argc != 2 && argc != 4) { + printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " + "(preferred duration, interval; in microsecods).\n" + "Optional second pair can be used to provide " + "acceptable values.\n"); + return -1; + } + + if (argc == 4) + res = os_snprintf(cmd, sizeof(cmd), + "P2P_PRESENCE_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ"); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 0 && argc != 2) { + printf("Invalid P2P_EXT_LISTEN command: needs two arguments " + "(availability period, availability interval; in " + "millisecods).\n" + "Extended Listen Timing can be cancelled with this " + "command when used without parameters.\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN"); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_INTERWORKING +static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "FETCH_ANQP"); +} + + +static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP"); +} + + +static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc == 0) + return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT"); + + res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 1) { + printf("Invalid INTERWORKING_CONNECT commands: needs one " + "argument (BSSID)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) return -1; - printf("%s", buf); - - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - *pos = '\0'; - os_strlcpy(addr, buf, addr_len); - return 0; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char addr[32], cmd[64]; + char cmd[100]; + int res; - if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) - return 0; - do { - os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + if (argc != 2) { + printf("Invalid ANQP_GET command: needs two arguments " + "(addr and info id list)\n"); + return -1; + } - return -1; + res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); } -#endif /* CONFIG_AP */ +#endif /* CONFIG_INTERWORKING */ -static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - return wpa_ctrl_command(ctrl, "SUSPEND"); + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid STA_AUTOCONNECT command: needs one argument " + "(0/1 = disable/enable automatic reconnection)\n"); + return -1; + } + res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long STA_AUTOCONNECT command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - return wpa_ctrl_command(ctrl, "RESUME"); + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid TDLS_DISCOVER command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long TDLS_DISCOVER command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - return wpa_ctrl_command(ctrl, "DROP_SA"); + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid TDLS_SETUP command: needs one argument " + "(Peer STA MAC address)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long TDLS_SETUP command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[128]; + char cmd[256]; int res; if (argc != 1) { - printf("Invalid ROAM command: needs one argument " - "(target AP's BSSID)\n"); + printf("Invalid TDLS_TEARDOWN command: needs one argument " + "(Peer STA MAC address)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); + res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long ROAM command.\n"); + printf("Too long TDLS_TEARDOWN command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } +static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SIGNAL_POLL"); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -1479,6 +2689,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "ping", wpa_cli_cmd_ping, cli_cmd_flag_none, "= pings wpa_supplicant" }, + { "relog", wpa_cli_cmd_relog, + cli_cmd_flag_none, + "= re-open log-file (allow rolling logs)" }, + { "note", wpa_cli_cmd_note, + cli_cmd_flag_none, + " = add a note to wpa_supplicant debug log" }, { "mib", wpa_cli_cmd_mib, cli_cmd_flag_none, "= get MIB variables (dot1x, dot11)" }, @@ -1501,6 +2717,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, "= set variables (shows list of variables when run without " "arguments)" }, + { "get", wpa_cli_cmd_get, + cli_cmd_flag_none, + " = get information" }, { "logon", wpa_cli_cmd_logon, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, @@ -1539,6 +2758,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "bssid", wpa_cli_cmd_bssid, cli_cmd_flag_none, " = set preferred BSSID for an SSID" }, + { "blacklist", wpa_cli_cmd_blacklist, + cli_cmd_flag_none, + " = add a BSSID to the blacklist\n" + "blacklist clear = clear the blacklist\n" + "blacklist = display the blacklist" }, + { "log_level", wpa_cli_cmd_log_level, + cli_cmd_flag_none, + " [] = update the log level/timestamp\n" + "log_level = display the current log level and log options" }, { "list_networks", wpa_cli_cmd_list_networks, cli_cmd_flag_none, "= list configured networks" }, @@ -1607,6 +2835,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "ap_scan", wpa_cli_cmd_ap_scan, cli_cmd_flag_none, " = set ap_scan parameter" }, + { "scan_interval", wpa_cli_cmd_scan_interval, + cli_cmd_flag_none, + " = set scan_interval parameter (in seconds)" }, + { "bss_expire_age", wpa_cli_cmd_bss_expire_age, + cli_cmd_flag_none, + " = set BSS expiration age parameter" }, + { "bss_expire_count", wpa_cli_cmd_bss_expire_count, + cli_cmd_flag_none, + " = set BSS expiration scan count parameter" }, { "stkstart", wpa_cli_cmd_stkstart, cli_cmd_flag_none, " = request STK negotiation with " }, @@ -1620,6 +2857,11 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_sensitive, " [PIN] = start WPS PIN method (returns PIN, if not " "hardcoded)" }, + { "wps_check_pin", wpa_cli_cmd_wps_check_pin, + cli_cmd_flag_sensitive, + " = verify PIN checksum" }, + { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none, + "Cancels the pending WPS operation" }, #ifdef CONFIG_WPS_OOB { "wps_oob", wpa_cli_cmd_wps_oob, cli_cmd_flag_sensitive, @@ -1628,9 +2870,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_reg", wpa_cli_cmd_wps_reg, cli_cmd_flag_sensitive, " = start WPS Registrar to configure an AP" }, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, + cli_cmd_flag_sensitive, + "[params..] = enable/disable AP PIN" }, { "wps_er_start", wpa_cli_cmd_wps_er_start, cli_cmd_flag_none, - "= start Wi-Fi Protected Setup External Registrar" }, + "[IP address] = start Wi-Fi Protected Setup External Registrar" }, { "wps_er_stop", wpa_cli_cmd_wps_er_stop, cli_cmd_flag_none, "= stop Wi-Fi Protected Setup External Registrar" }, @@ -1643,6 +2888,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_er_learn", wpa_cli_cmd_wps_er_learn, cli_cmd_flag_sensitive, " = learn AP configuration" }, + { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, + cli_cmd_flag_none, + " = set AP configuration for enrolling" }, + { "wps_er_config", wpa_cli_cmd_wps_er_config, + cli_cmd_flag_sensitive, + " = configure AP" }, { "ibss_rsn", wpa_cli_cmd_ibss_rsn, cli_cmd_flag_none, " = request RSN authentication with in IBSS" }, @@ -1663,6 +2914,104 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "roam", wpa_cli_cmd_roam, cli_cmd_flag_none, " = roam to the specified BSS" }, +#ifdef CONFIG_P2P + { "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none, + "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, + { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none, + "= stop P2P Devices search" }, + { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none, + " <\"pbc\"|PIN> = connect to a P2P Devices" }, + { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none, + "[timeout] = listen for P2P Devices for up-to timeout seconds" }, + { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none, + " = remove P2P group interface (terminate group if GO)" }, + { "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none, + "= add a new P2P group (local end as GO)" }, + { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none, + " = request provisioning discovery" }, + { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, + cli_cmd_flag_none, + "= get the passphrase for a group (GO only)" }, + { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req, + cli_cmd_flag_none, + " = schedule service discovery request" }, + { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req, + cli_cmd_flag_none, + " = cancel pending service discovery request" }, + { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, + cli_cmd_flag_none, + " = service discovery response" }, + { "p2p_service_update", wpa_cli_cmd_p2p_service_update, + cli_cmd_flag_none, + "= indicate change in local services" }, + { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, + cli_cmd_flag_none, + " = set external processing of service discovery" }, + { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, + cli_cmd_flag_none, + "= remove all stored service entries" }, + { "p2p_service_add", wpa_cli_cmd_p2p_service_add, + cli_cmd_flag_none, + " = add a local " + "service" }, + { "p2p_service_del", wpa_cli_cmd_p2p_service_del, + cli_cmd_flag_none, + " [|service] = remove a local " + "service" }, + { "p2p_reject", wpa_cli_cmd_p2p_reject, + cli_cmd_flag_none, + " = reject connection attempts from a specific peer" }, + { "p2p_invite", wpa_cli_cmd_p2p_invite, + cli_cmd_flag_none, + " [peer=addr] = invite peer" }, + { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none, + "[discovered] = list known (optionally, only fully discovered) P2P " + "peers" }, + { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none, + "
= show information about known P2P peer" }, + { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none, + " = set a P2P parameter" }, + { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none, + "= flush P2P state" }, + { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none, + "= cancel P2P group formation" }, + { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none, + "
= unauthorize a peer" }, + { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none, + "[ ] [ ] = request GO " + "presence" }, + { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none, + "[ ] = set extended listen timing" }, +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none, + "= fetch ANQP information for all APs" }, + { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none, + "= stop fetch_anqp operation" }, + { "interworking_select", wpa_cli_cmd_interworking_select, + cli_cmd_flag_none, + "[auto] = perform Interworking network selection" }, + { "interworking_connect", wpa_cli_cmd_interworking_connect, + cli_cmd_flag_none, + " = connect using Interworking credentials" }, + { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none, + " [,]... = request ANQP information" }, +#endif /* CONFIG_INTERWORKING */ + { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none, + "<0/1> = disable/enable automatic reconnection" }, + { "tdls_discover", wpa_cli_cmd_tdls_discover, + cli_cmd_flag_none, + " = request TDLS discovery with " }, + { "tdls_setup", wpa_cli_cmd_tdls_setup, + cli_cmd_flag_none, + " = request TDLS setup with " }, + { "tdls_teardown", wpa_cli_cmd_tdls_teardown, + cli_cmd_flag_none, + " = tear down TDLS with " }, + { "signal_poll", wpa_cli_cmd_signal_poll, + cli_cmd_flag_none, + "= get signal parameters" }, { NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -1694,8 +3043,7 @@ static void print_help(void) } -#ifdef CONFIG_READLINE -static int cmd_has_sensitive_data(const char *cmd) +static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd) { const char *c, *delim; int n; @@ -1714,7 +3062,76 @@ static int cmd_has_sensitive_data(const char *cmd) } return 0; } -#endif /* CONFIG_READLINE */ + + +static char ** wpa_list_cmd_list(void) +{ + char **res; + int i, count; + + count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]); + res = os_zalloc(count * sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; wpa_cli_commands[i].cmd; i++) { + res[i] = os_strdup(wpa_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** wpa_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + if (os_strcasecmp(cmd, "bss") == 0) + return wpa_cli_complete_bss(str, pos); +#ifdef CONFIG_P2P + if (os_strcasecmp(cmd, "p2p_connect") == 0) + return wpa_cli_complete_p2p_connect(str, pos); + if (os_strcasecmp(cmd, "p2p_peer") == 0) + return wpa_cli_complete_p2p_peer(str, pos); + if (os_strcasecmp(cmd, "p2p_group_remove") == 0) + return wpa_cli_complete_p2p_group_remove(str, pos); +#endif /* CONFIG_P2P */ + + for (i = 0; wpa_cli_commands[i].cmd; i++) { + if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) { + edit_clear_line(); + printf("\r%s\n", wpa_cli_commands[i].usage); + edit_redraw(); + break; + } + } + + return NULL; +} + + +static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return wpa_list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = wpa_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1852,6 +3269,18 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_connected = 0; wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); } + } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; @@ -1874,10 +3303,76 @@ static void wpa_cli_reconnect(void) } -static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, - int action_monitor) +static void cli_event(const char *str) +{ + const char *start, *s; + + start = os_strchr(str, '>'); + if (start == NULL) + return; + + start++; + + if (str_starts(start, WPA_EVENT_BSS_ADDED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + s = os_strchr(s + 1, ' '); + if (s == NULL) + return; + cli_txt_list_add(&bsses, s + 1); + return; + } + + if (str_starts(start, WPA_EVENT_BSS_REMOVED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + s = os_strchr(s + 1, ' '); + if (s == NULL) + return; + cli_txt_list_del_addr(&bsses, s + 1); + return; + } + +#ifdef CONFIG_P2P + if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) { + s = os_strstr(start, " p2p_dev_addr="); + if (s == NULL) + return; + cli_txt_list_add_addr(&p2p_peers, s + 14); + return; + } + + if (str_starts(start, P2P_EVENT_DEVICE_LOST)) { + s = os_strstr(start, " p2p_dev_addr="); + if (s == NULL) + return; + cli_txt_list_del_addr(&p2p_peers, s + 14); + return; + } + + if (str_starts(start, P2P_EVENT_GROUP_STARTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_add_word(&p2p_groups, s + 1); + return; + } + + if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_del_word(&p2p_groups, s + 1); + return; + } +#endif /* CONFIG_P2P */ +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) { - int first = 1; if (ctrl_conn == NULL) { wpa_cli_reconnect(); return; @@ -1890,14 +3385,12 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, if (action_monitor) wpa_cli_action_process(buf); else { - if (in_read && first) - printf("\r"); - first = 0; - printf("%s\n", buf); -#ifdef CONFIG_READLINE - rl_on_new_line(); - rl_redisplay(); -#endif /* CONFIG_READLINE */ + cli_event(buf); + if (wpa_cli_show_event(buf)) { + edit_clear_line(); + printf("\r%s\n", buf); + edit_redraw(); + } } } else { printf("Could not read pending message.\n"); @@ -1912,214 +3405,108 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, } } +#define max_args 10 -#ifdef CONFIG_READLINE -static char * wpa_cli_cmd_gen(const char *text, int state) +static int tokenize_cmd(char *cmd, char *argv[]) { - static int i, len; - const char *cmd; - - if (state == 0) { - i = 0; - len = os_strlen(text); - } + char *pos; + int argc = 0; - while ((cmd = wpa_cli_commands[i].cmd)) { - i++; - if (os_strncasecmp(cmd, text, len) == 0) - return strdup(cmd); + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; } - return NULL; + return argc; } -static char * wpa_cli_dummy_gen(const char *text, int state) +static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) { - int i; - - for (i = 0; wpa_cli_commands[i].cmd; i++) { - const char *cmd = wpa_cli_commands[i].cmd; - size_t len = os_strlen(cmd); - if (os_strncasecmp(rl_line_buffer, cmd, len) == 0 && - rl_line_buffer[len] == ' ') { - printf("\n%s\n", wpa_cli_commands[i].usage); - rl_on_new_line(); - rl_redisplay(); - break; - } + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); } - - rl_attempted_completion_over = 1; - return NULL; + if (!ctrl_conn) + wpa_cli_reconnect(); + eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); } -static char * wpa_cli_status_gen(const char *text, int state) +static void wpa_cli_eloop_terminate(int sig, void *signal_ctx) { - static int i, len; - char *options[] = { - "verbose", NULL - }; - char *t; - - if (state == 0) { - i = 0; - len = os_strlen(text); - } + eloop_terminate(); +} - while ((t = options[i])) { - i++; - if (os_strncasecmp(t, text, len) == 0) - return strdup(t); - } - rl_attempted_completion_over = 1; - return NULL; +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + wpa_cli_recv_pending(mon_conn, 0); } -static char ** wpa_cli_completion(const char *text, int start, int end) +static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd) { - char * (*func)(const char *text, int state); + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); +} - if (start == 0) - func = wpa_cli_cmd_gen; - else if (os_strncasecmp(rl_line_buffer, "status ", 7) == 0) - func = wpa_cli_status_gen; - else - func = wpa_cli_dummy_gen; - return rl_completion_matches(text, func); + +static void wpa_cli_edit_eof_cb(void *ctx) +{ + eloop_terminate(); } -#endif /* CONFIG_READLINE */ static void wpa_cli_interactive(void) { -#define max_args 10 - char cmdbuf[256], *cmd, *argv[max_args], *pos; - int argc; -#ifdef CONFIG_READLINE char *home, *hfile = NULL; -#endif /* CONFIG_READLINE */ printf("\nInteractive mode\n\n"); -#ifdef CONFIG_READLINE - rl_attempted_completion_function = wpa_cli_completion; home = getenv("HOME"); if (home) { const char *fname = ".wpa_cli_history"; int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; hfile = os_malloc(hfile_len); - if (hfile) { - int res; - res = os_snprintf(hfile, hfile_len, "%s/%s", home, - fname); - if (res >= 0 && res < hfile_len) { - hfile[hfile_len - 1] = '\0'; - read_history(hfile); - stifle_history(100); - } - } + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); } -#endif /* CONFIG_READLINE */ - do { - wpa_cli_recv_pending(mon_conn, 0, 0); -#ifndef CONFIG_NATIVE_WINDOWS - alarm(ping_interval); -#endif /* CONFIG_NATIVE_WINDOWS */ -#ifdef CONFIG_WPA_CLI_FORK - if (mon_pid) - kill(mon_pid, SIGUSR1); -#endif /* CONFIG_WPA_CLI_FORK */ -#ifdef CONFIG_READLINE - cmd = readline("> "); - if (cmd && *cmd) { - HIST_ENTRY *h; - while (next_history()) - ; - h = previous_history(); - if (h == NULL || os_strcmp(cmd, h->line) != 0) - add_history(cmd); - next_history(); - } -#else /* CONFIG_READLINE */ - printf("> "); - cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin); -#endif /* CONFIG_READLINE */ -#ifndef CONFIG_NATIVE_WINDOWS - alarm(0); -#endif /* CONFIG_NATIVE_WINDOWS */ - if (cmd == NULL) - break; - wpa_cli_recv_pending(mon_conn, 0, 0); - pos = cmd; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - argc = 0; - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - if (*pos == '"') { - char *pos2 = os_strrchr(pos, '"'); - if (pos2) - pos = pos2 + 1; - } - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - if (argc) - wpa_request(ctrl_conn, argc, argv); - - if (cmd != cmdbuf) - free(cmd); -#ifdef CONFIG_WPA_CLI_FORK - if (mon_pid) - kill(mon_pid, SIGUSR2); -#endif /* CONFIG_WPA_CLI_FORK */ - } while (!wpa_cli_quit); - -#ifdef CONFIG_READLINE - if (hfile) { - /* Save command history, excluding lines that may contain - * passwords. */ - HIST_ENTRY *h; - history_set_pos(0); - while ((h = current_history())) { - char *p = h->line; - while (*p == ' ' || *p == '\t') - p++; - if (cmd_has_sensitive_data(p)) { - h = remove_history(where_history()); - if (h) { - os_free(h->line); - os_free(h->data); - os_free(h); - } else - next_history(); - } else - next_history(); - } - write_history(hfile); - os_free(hfile); - } -#endif /* CONFIG_READLINE */ + eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL); + edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, + wpa_cli_edit_completion_cb, NULL, hfile); + eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); + + eloop_run(); + + cli_txt_list_flush(&p2p_peers); + cli_txt_list_flush(&p2p_groups); + cli_txt_list_flush(&bsses); + edit_deinit(hfile, wpa_cli_edit_filter_history_cb); + os_free(hfile); + eloop_cancel_timeout(wpa_cli_ping, NULL, NULL); + wpa_cli_close_connection(); } @@ -2149,7 +3536,7 @@ static void wpa_cli_action(struct wpa_ctrl *ctrl) } if (FD_ISSET(fd, &rfds)) - wpa_cli_recv_pending(ctrl, 0, 1); + wpa_cli_recv_pending(ctrl, 1); else { /* verify that connection is still working */ len = sizeof(buf) - 1; @@ -2182,34 +3569,6 @@ static void wpa_cli_terminate(int sig) } -#ifdef CONFIG_WPA_CLI_FORK -static void wpa_cli_usr1(int sig) -{ -#ifdef CONFIG_READLINE - rl_on_new_line(); - rl_redisplay(); -#endif /* CONFIG_READLINE */ -} -#endif /* CONFIG_WPA_CLI_FORK */ - - -#ifndef CONFIG_NATIVE_WINDOWS -static void wpa_cli_alarm(int sig) -{ - if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { - printf("Connection to wpa_supplicant lost - trying to " - "reconnect\n"); - wpa_cli_close_connection(); - } - if (!ctrl_conn) - wpa_cli_reconnect(); - if (mon_conn) - wpa_cli_recv_pending(mon_conn, 1, 0); - alarm(ping_interval); -} -#endif /* CONFIG_NATIVE_WINDOWS */ - - static char * wpa_cli_get_default_ifname(void) { char *ifname = NULL; @@ -2217,8 +3576,17 @@ static char * wpa_cli_get_default_ifname(void) #ifdef CONFIG_CTRL_IFACE_UNIX struct dirent *dent; DIR *dir = opendir(ctrl_iface_dir); - if (!dir) + if (!dir) { +#ifdef ANDROID + char ifprop[PROPERTY_VALUE_MAX]; + if (property_get("wifi.interface", ifprop, NULL) != 0) { + ifname = os_strdup(ifprop); + printf("Using interface '%s'\n", ifname); + return ifname; + } +#endif /* ANDROID */ return NULL; + } while ((dent = readdir(dir))) { #ifdef _DIRENT_HAVE_D_TYPE /* @@ -2320,6 +3688,9 @@ int main(int argc, char *argv[]) if (interactive) printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + if (eloop_init()) + return -1; + if (global) { #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE ctrl_conn = wpa_ctrl_open(NULL); @@ -2337,12 +3708,6 @@ int main(int argc, char *argv[]) signal(SIGINT, wpa_cli_terminate); signal(SIGTERM, wpa_cli_terminate); #endif /* _WIN32_WCE */ -#ifndef CONFIG_NATIVE_WINDOWS - signal(SIGALRM, wpa_cli_alarm); -#endif /* CONFIG_NATIVE_WINDOWS */ -#ifdef CONFIG_WPA_CLI_FORK - signal(SIGUSR1, wpa_cli_usr1); -#endif /* CONFIG_WPA_CLI_FORK */ if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); @@ -2393,6 +3758,7 @@ int main(int argc, char *argv[]) ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); os_free(ctrl_ifname); + eloop_destroy(); wpa_cli_cleanup(); return ret; diff --git a/wpa_supplicant/wpa_gui-qt4/.gitignore b/wpa_supplicant/wpa_gui-qt4/.gitignore index efcb666..da818cb 100644 --- a/wpa_supplicant/wpa_gui-qt4/.gitignore +++ b/wpa_supplicant/wpa_gui-qt4/.gitignore @@ -1,6 +1,4 @@ .moc .obj .ui -Makefile -wpa_gui qrc_icons.cpp diff --git a/wpa_supplicant/wpa_gui-qt4/icons.qrc b/wpa_supplicant/wpa_gui-qt4/icons.qrc index 316ee89..dd72c7e 100644 --- a/wpa_supplicant/wpa_gui-qt4/icons.qrc +++ b/wpa_supplicant/wpa_gui-qt4/icons.qrc @@ -3,5 +3,7 @@ icons/wpa_gui.svg icons/ap.svg icons/laptop.svg + icons/group.svg + icons/invitation.svg diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile new file mode 100644 index 0000000..709514c --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make -f + +NAMES := wpa_gui ap laptop group invitation +SIZES := 16x16 22x22 32x32 48x48 64x64 128x128 +ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name)))) +ICONS += $(addsuffix .xpm, $(NAMES)) + +all: $(ICONS) + +%.png: + mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/ + inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \ + --export-width=$(word 1, $(subst x, , $(@))) \ + --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \ + --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@)) + +%.xpm: + mkdir -p pixmaps/ + convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm) + convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@ + +clean: + $(RM) -r pixmaps hicolor diff --git a/wpa_supplicant/wpa_gui-qt4/icons/README b/wpa_supplicant/wpa_gui-qt4/icons/README index d73eed5..3953238 100644 --- a/wpa_supplicant/wpa_gui-qt4/icons/README +++ b/wpa_supplicant/wpa_gui-qt4/icons/README @@ -37,3 +37,38 @@ by: metalmarious last change: May 18, 2008 07:04 pm (File added) date: August 27, 2007 04:44 am license: PD + + +group.svg +--------- + +http://www.openclipart.org/detail/25428 +http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg +Uploader: + Anonymous +Drawn by: + Andrew Fitzsimon / Anonymous +Created: + 2009-04-29 04:07:37 +Description: + A network icon by Andrew Fitzsimon. Etiquette Icon set. + From 0.18 OCAL database. + +Public Domain + + + +invitation.svg +-------------- + +http://www.openclipart.org/detail/974 +http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg +Uploader: + jean_victor_balin +Drawn by: + jean_victor_balin +Created: + 2006-10-27 02:12:13 +Description: + +Public Domain diff --git a/wpa_supplicant/wpa_gui-qt4/icons/group.svg b/wpa_supplicant/wpa_gui-qt4/icons/group.svg new file mode 100644 index 0000000..4ea959b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/group.svg @@ -0,0 +1,616 @@ + + + + + + + Etiquette Icons + + + + hash + + filesystem + computer + icons + + + + + Andy Fitzsimon + + + + + Andy Fitzsimon + + + + + Andy Fitzsimon + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg new file mode 100644 index 0000000..1a02d13 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Green Unknown + 2005-11-01 + + + Jean-Victor Balin + + + jean.victor.balin@gmail.com + + + + icon + + + + + + + + + + + + + + + + + + + + diff --git a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc index f262217..9a30b7f 100644 --- a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc +++ b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc @@ -3,5 +3,7 @@ icons/hicolor/16x16/apps/wpa_gui.png icons/hicolor/32x32/apps/ap.png icons/hicolor/32x32/apps/laptop.png + icons/hicolor/32x32/apps/group.png + icons/hicolor/32x32/apps/invitation.png diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp index 7a99299..65bb17d 100644 --- a/wpa_supplicant/wpa_gui-qt4/peers.cpp +++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp @@ -1,6 +1,6 @@ /* * wpa_gui - Peers class - * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2009-2010, 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 @@ -27,11 +27,22 @@ enum { peer_role_type, peer_role_uuid, peer_role_details, + peer_role_ifname, peer_role_pri_dev_type, peer_role_ssid, peer_role_config_methods, peer_role_dev_passwd_id, - peer_role_bss_id + peer_role_bss_id, + peer_role_selected_method, + peer_role_selected_pin, + peer_role_requested_method, + peer_role_network_id +}; + +enum selected_method { + SEL_METHOD_NONE, + SEL_METHOD_PIN_PEER_DISPLAY, + SEL_METHOD_PIN_LOCAL_DISPLAY }; /* @@ -44,6 +55,12 @@ enum peer_type { PEER_TYPE_AP, PEER_TYPE_AP_WPS, PEER_TYPE_WPS_PIN_NEEDED, + PEER_TYPE_P2P, + PEER_TYPE_P2P_CLIENT, + PEER_TYPE_P2P_GROUP, + PEER_TYPE_P2P_PERSISTENT_GROUP_GO, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT, + PEER_TYPE_P2P_INVITATION, PEER_TYPE_WPS_ER_AP, PEER_TYPE_WPS_ER_AP_UNCONFIGURED, PEER_TYPE_WPS_ER_ENROLLEE, @@ -61,20 +78,27 @@ Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags) default_icon = new QIcon(":/icons/wpa_gui.svg"); ap_icon = new QIcon(":/icons/ap.svg"); laptop_icon = new QIcon(":/icons/laptop.svg"); + group_icon = new QIcon(":/icons/group.svg"); + invitation_icon = new QIcon(":/icons/invitation.svg"); } else { default_icon = new QIcon(":/icons/wpa_gui.png"); ap_icon = new QIcon(":/icons/ap.png"); laptop_icon = new QIcon(":/icons/laptop.png"); + group_icon = new QIcon(":/icons/group.png"); + invitation_icon = new QIcon(":/icons/invitation.png"); } peers->setModel(&model); peers->setResizeMode(QListView::Adjust); + peers->setDragEnabled(false); + peers->setSelectionMode(QAbstractItemView::NoSelection); peers->setContextMenuPolicy(Qt::CustomContextMenu); connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(context_menu(const QPoint &))); wpagui = NULL; + hide_ap = false; } @@ -90,6 +114,8 @@ Peers::~Peers() delete default_icon; delete ap_icon; delete laptop_icon; + delete group_icon; + delete invitation_icon; } @@ -115,6 +141,24 @@ QString Peers::ItemType(int type) case PEER_TYPE_WPS_PIN_NEEDED: title = tr("WPS PIN needed"); break; + case PEER_TYPE_P2P: + title = tr("P2P Device"); + break; + case PEER_TYPE_P2P_CLIENT: + title = tr("P2P Device (group client)"); + break; + case PEER_TYPE_P2P_GROUP: + title = tr("P2P Group"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_GO: + title = tr("P2P Persistent Group (GO)"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT: + title = tr("P2P Persistent Group (client)"); + break; + case PEER_TYPE_P2P_INVITATION: + title = tr("P2P Invitation"); + break; case PEER_TYPE_WPS_ER_AP: title = tr("ER: WPS AP"); break; @@ -150,6 +194,11 @@ void Peers::context_menu(const QPoint &pos) if (var.isValid()) config_methods = var.toInt(); + enum selected_method method = SEL_METHOD_NONE; + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + if ((type == PEER_TYPE_ASSOCIATED_STATION || type == PEER_TYPE_AP_WPS || type == PEER_TYPE_WPS_PIN_NEEDED || @@ -160,6 +209,60 @@ void Peers::context_menu(const QPoint &pos) SLOT(enter_pin())); } + if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) { + menu->addAction(tr("P2P Connect"), this, + SLOT(ctx_p2p_connect())); + if (method == SEL_METHOD_NONE && + config_methods > -1 && + config_methods & 0x0080 /* PBC */ && + config_methods != 0x0080) + menu->addAction(tr("P2P Connect (PBC)"), this, + SLOT(connect_pbc())); + if (method == SEL_METHOD_NONE) { + menu->addAction(tr("P2P Request PIN"), this, + SLOT(ctx_p2p_req_pin())); + menu->addAction(tr("P2P Show PIN"), this, + SLOT(ctx_p2p_show_pin())); + } + + if (config_methods > -1 && (config_methods & 0x0100)) { + /* Peer has Keypad */ + menu->addAction(tr("P2P Display PIN"), this, + SLOT(ctx_p2p_display_pin())); + } + + if (config_methods > -1 && (config_methods & 0x000c)) { + /* Peer has Label or Display */ + menu->addAction(tr("P2P Enter PIN"), this, + SLOT(ctx_p2p_enter_pin())); + } + } + + if (type == PEER_TYPE_P2P_GROUP) { + menu->addAction(tr("Show passphrase"), this, + SLOT(ctx_p2p_show_passphrase())); + menu->addAction(tr("Remove P2P Group"), this, + SLOT(ctx_p2p_remove_group())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT || + type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Start group"), this, + SLOT(ctx_p2p_start_persistent())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) { + menu->addAction(tr("Invite"), this, + SLOT(ctx_p2p_invite())); + } + + if (type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Ignore"), this, + SLOT(ctx_p2p_delete())); + } + if (type == PEER_TYPE_AP_WPS) { menu->addAction(tr("Connect (PBC)"), this, SLOT(connect_pbc())); @@ -183,6 +286,20 @@ void Peers::context_menu(const QPoint &pos) ctx_item = NULL; menu->addAction(QString(tr("Refresh")), this, SLOT(ctx_refresh())); + menu->addAction(tr("Start P2P discovery"), this, + SLOT(ctx_p2p_start())); + menu->addAction(tr("Stop P2P discovery"), this, + SLOT(ctx_p2p_stop())); + menu->addAction(tr("P2P listen only"), this, + SLOT(ctx_p2p_listen())); + menu->addAction(tr("Start P2P group"), this, + SLOT(ctx_p2p_start_group())); + if (hide_ap) + menu->addAction(tr("Show AP entries"), this, + SLOT(ctx_show_ap())); + else + menu->addAction(tr("Hide AP entries"), this, + SLOT(ctx_hide_ap())); } menu->exec(peers->mapToGlobal(pos)); @@ -197,10 +314,9 @@ void Peers::enter_pin() int peer_type = ctx_item->data(peer_role_type).toInt(); QString uuid; QString addr; + addr = ctx_item->data(peer_role_address).toString(); if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) uuid = ctx_item->data(peer_role_uuid).toString(); - else - addr = ctx_item->data(peer_role_address).toString(); StringQuery input(tr("PIN:")); input.setWindowTitle(tr("PIN for ") + ctx_item->text()); @@ -212,9 +328,10 @@ void Peers::enter_pin() size_t reply_len; if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { - snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", uuid.toAscii().constData(), - input.get_string().toAscii().constData()); + input.get_string().toAscii().constData(), + addr.toAscii().constData()); } else { snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", addr.toAscii().constData(), @@ -236,6 +353,60 @@ void Peers::ctx_refresh() } +void Peers::ctx_p2p_start() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P discovery."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_stop() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len); +} + + +void Peers::ctx_p2p_listen() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P listen."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_start_group() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P group."); + msg.exec(); + } +} + + void Peers::add_station(QString info) { QStringList lines = info.split(QRegExp("\\n")); @@ -249,6 +420,8 @@ void Peers::add_station(QString info) if ((*it).startsWith("wpsDeviceName=")) name = (*it).mid(pos); + else if ((*it).startsWith("p2p_device_name=")) + name = (*it).mid(pos); } if (name.isEmpty()) @@ -256,6 +429,24 @@ void Peers::add_station(QString info) QStandardItem *item = new QStandardItem(*laptop_icon, name); if (item) { + /* Remove WPS enrollee entry if one is still pending */ + if (model.rowCount() > 0) { + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_address, + lines[0]); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item; + item = model.itemFromIndex(lst[i]); + if (item == NULL) + continue; + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_WPS_ENROLLEE) { + model.removeRow(lst[i].row()); + break; + } + } + } + item->setData(lines[0], peer_role_address); item->setData(PEER_TYPE_ASSOCIATED_STATION, peer_role_type); @@ -322,6 +513,55 @@ void Peers::add_single_station(const char *addr) } +void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params) +{ + /* + * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0 + * dev_type=1-0050f204-1 dev_name='Wireless Client' + * config_methods=0x8c + */ + + QStringList items = + params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = ""; + QString name = ""; + int config_methods = 0; + QString dev_type; + + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + int pos = str.indexOf('=') + 1; + if (str.startsWith("dev_name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("dev=")) + addr = str.mid(pos); + else if (str.startsWith("dev_type=") && dev_type.isEmpty()) + dev_type = str.mid(pos); + } + + QStandardItem *item = find_addr(addr); + if (item) + return; + + item = new QStandardItem(*default_icon, name); + if (item) { + /* TODO: indicate somehow the relationship to the group owner + * (parent) */ + item->setData(addr, peer_role_address); + item->setData(config_methods, peer_role_config_methods); + item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type); + if (!dev_type.isEmpty()) + item->setData(dev_type, peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT)); + model.appendRow(item); + } +} + + void Peers::remove_bss(int id) { if (model.rowCount() == 0) @@ -340,6 +580,9 @@ bool Peers::add_bss(const char *cmd) char reply[2048]; size_t reply_len; + if (hide_ap) + return false; + reply_len = sizeof(reply) - 1; if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) return false; @@ -403,6 +646,14 @@ bool Peers::add_bss(const char *cmd) if (!ssid.isEmpty()) item->setData(ssid, peer_role_ssid); model.appendRow(item); + + lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + if ((*it).startsWith("p2p_group_client:")) + add_p2p_group_client(item, + (*it).mid(18)); + } } return true; @@ -426,6 +677,95 @@ void Peers::add_scan_results() } +void Peers::add_persistent(int id, const char *ssid, const char *bssid) +{ + char cmd[100]; + char reply[100]; + size_t reply_len; + int mode; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + mode = atoi(reply); + + QString name = ssid; + name = '[' + name + ']'; + + QStandardItem *item = new QStandardItem(*group_icon, name); + if (!item) + return; + + int type; + if (mode == 3) + type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO; + else + type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT; + item->setData(type, peer_role_type); + item->setToolTip(ItemType(type)); + item->setData(ssid, peer_role_ssid); + if (bssid && strcmp(bssid, "any") == 0) + bssid = NULL; + if (bssid) + item->setData(bssid, peer_role_address); + item->setData(id, peer_role_network_id); + item->setBackground(Qt::BDiagPattern); + + model.appendRow(item); +} + + +void Peers::add_persistent_groups() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + + len = sizeof(buf) - 1; + if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) + add_persistent(atoi(id), ssid, bssid); + + if (last) + break; + start = end + 1; + } +} + + void Peers::update_peers() { model.clear(); @@ -438,6 +778,7 @@ void Peers::update_peers() add_stations(); add_scan_results(); + add_persistent_groups(); } @@ -454,6 +795,22 @@ QStandardItem * Peers::find_addr(QString addr) } +QStandardItem * Peers::find_addr_type(QString addr, int type) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_address, + addr); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item->data(peer_role_type).toInt() == type) + return item; + } + return NULL; +} + + QStandardItem * Peers::find_uuid(QString uuid) { if (model.rowCount() == 0) @@ -530,16 +887,225 @@ void Peers::event_notify(WpaMsg msg) return; QModelIndexList lst = model.match(model.index(0, 0), - peer_role_address, addr); + peer_role_address, addr, -1); for (int i = 0; i < lst.size(); i++) { QStandardItem *item = model.itemFromIndex(lst[i]); if (item && item->data(peer_role_type).toInt() == - PEER_TYPE_ASSOCIATED_STATION) + PEER_TYPE_ASSOCIATED_STATION) { model.removeRow(lst[i].row()); + break; + } + } + return; + } + + if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) { + /* + * P2P-DEVICE-FOUND 02:b5:64:63:30:63 + * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1 + * name='Wireless Client' config_methods=0x84 dev_capab=0x21 + * group_capab=0x0 + */ + QStringList items = + text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = items[1]; + QString name = ""; + QString pri_dev_type; + int config_methods = 0; + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + if (str.startsWith("name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("pri_dev_type=")) + pri_dev_type = str.section('=', 1); + } + + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P) + return; + } + + item = new QStandardItem(*default_icon, name); + if (item) { + item->setData(addr, peer_role_address); + item->setData(config_methods, + peer_role_config_methods); + item->setData(PEER_TYPE_P2P, peer_role_type); + if (!pri_dev_type.isEmpty()) + item->setData(pri_dev_type, + peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P)); + model.appendRow(item); + } + + item = find_addr_type(addr, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT); + if (item) + item->setBackground(Qt::NoBrush); + } + + if (text.startsWith(P2P_EVENT_GROUP_STARTED)) { + /* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F" + * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7 + * [PERSISTENT] */ + QStringList items = text.split(' '); + if (items.size() < 4) + return; + + int pos = text.indexOf(" ssid=\""); + if (pos < 0) + return; + QString ssid = text.mid(pos + 7); + pos = ssid.indexOf(" passphrase=\""); + if (pos < 0) + pos = ssid.indexOf(" psk="); + if (pos >= 0) + ssid.truncate(pos); + pos = ssid.lastIndexOf('"'); + if (pos >= 0) + ssid.truncate(pos); + + QStandardItem *item = new QStandardItem(*group_icon, ssid); + if (item) { + item->setData(PEER_TYPE_P2P_GROUP, peer_role_type); + item->setData(items[1], peer_role_ifname); + QString details; + if (items[2] == "GO") { + details = tr("P2P GO for interface ") + + items[1]; + } else { + details = tr("P2P client for interface ") + + items[1]; + } + if (text.contains(" [PERSISTENT]")) + details += "\nPersistent group"; + item->setData(details, peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP)); + model.appendRow(item); + } + } + + if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) { + /* P2P-GROUP-REMOVED wlan0-p2p-0 GO */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_ifname, items[1]); + for (int i = 0; i < lst.size(); i++) + model.removeRow(lst[i].row()); + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) { + /* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + QString addr = items[1]; + QString pin = items[2]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_selected_method); + item->setData(pin, peer_role_selected_pin); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) { + ctx_item = item; + ctx_p2p_display_pin_pd(); + } + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) { + /* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + QString addr = items[1]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_selected_method); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) { + ctx_item = item; + ctx_p2p_connect(); } return; } + if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) { + /* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + if (!items[1].startsWith("sa=") || + !items[2].startsWith("persistent=")) + return; + QString addr = items[1].mid(3); + int id = items[2].mid(11).toInt(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + QString name; + char *pos = strrchr(reply, '"'); + if (pos && reply[0] == '"') { + *pos = '\0'; + name = reply + 1; + } else + name = reply; + + QStandardItem *item; + item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION); + if (item) + model.removeRow(item->row()); + + item = new QStandardItem(*invitation_icon, name); + if (!item) + return; + item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION)); + item->setData(addr, peer_role_address); + item->setData(id, peer_role_network_id); + + model.appendRow(item); + + enable_persistent(id); + + return; + } + + if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) { + /* P2P-INVITATION-RESULT status=1 */ + /* TODO */ + return; + } + if (text.startsWith(WPS_EVENT_ER_AP_ADD)) { /* * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 @@ -700,6 +1266,13 @@ void Peers::event_notify(WpaMsg msg) int dev_passwd_id = items[5].toInt(); QString name; + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_ASSOCIATED_STATION) + return; /* already associated */ + } + int pos = text.indexOf('['); if (pos >= 0) { int pos2 = text.lastIndexOf(']'); @@ -713,8 +1286,6 @@ void Peers::event_notify(WpaMsg msg) if (name.isEmpty()) name = addr; - QStandardItem *item; - item = find_uuid(uuid); if (item) { QVariant var = item->data(peer_role_config_methods); @@ -767,6 +1338,221 @@ void Peers::event_notify(WpaMsg msg) } +void Peers::ctx_p2p_connect() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + int config_methods = + ctx_item->data(peer_role_config_methods).toInt(); + enum selected_method method = SEL_METHOD_NONE; + QVariant var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) { + arg = ctx_item->data(peer_role_selected_pin).toString(); + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); + } else if (method == SEL_METHOD_PIN_PEER_DISPLAY) { + StringQuery input(tr("PIN from peer display:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } else if (config_methods == 0x0080 /* PBC */) { + arg = "pbc"; + } else { + StringQuery input(tr("PIN:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_req_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request PIN from peer.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_show_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request peer to enter PIN.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_display_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin", + addr.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + reply); +} + + +void Peers::ctx_p2p_display_pin_pd() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg = ctx_item->data(peer_role_selected_pin).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); +} + + +void Peers::ctx_p2p_enter_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + + StringQuery input(tr("PIN from peer:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad", + addr.toAscii().constData(), + arg.toAscii().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_remove_group() +{ + if (ctx_item == NULL) + return; + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", + ctx_item->data(peer_role_ifname).toString().toAscii(). + constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to remove P2P Group."); + msg.exec(); + } +} + + void Peers::closeEvent(QCloseEvent *) { if (wpagui) { @@ -864,6 +1650,27 @@ void Peers::properties() info += "\n"; } + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) { + enum selected_method method = + (enum selected_method) var.toInt(); + switch (method) { + case SEL_METHOD_NONE: + break; + case SEL_METHOD_PIN_PEER_DISPLAY: + info += tr("Selected Method: PIN on peer display\n"); + break; + case SEL_METHOD_PIN_LOCAL_DISPLAY: + info += tr("Selected Method: PIN on local display\n"); + break; + } + } + + var = ctx_item->data(peer_role_selected_pin); + if (var.isValid()) { + info += tr("PIN to enter on peer: ") + var.toString() + "\n"; + } + var = ctx_item->data(peer_role_dev_passwd_id); if (var.isValid()) { info += tr("Device Password ID: ") + var.toString(); @@ -914,6 +1721,11 @@ void Peers::connect_pbc() snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", ctx_item->data(peer_role_uuid).toString().toAscii(). constData()); + } else if (peer_type == PEER_TYPE_P2P || + peer_type == PEER_TYPE_P2P_CLIENT) { + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc", + ctx_item->data(peer_role_address).toString(). + toAscii().constData()); } else { snprintf(cmd, sizeof(cmd), "WPS_PBC"); } @@ -954,3 +1766,124 @@ void Peers::learn_ap_config() msg.exec(); } } + + +void Peers::ctx_hide_ap() +{ + hide_ap = true; + + if (model.rowCount() == 0) + return; + + do { + QModelIndexList lst; + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP); + if (lst.size() == 0) { + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP_WPS); + if (lst.size() == 0) + break; + } + + model.removeRow(lst[0].row()); + } while (1); +} + + +void Peers::ctx_show_ap() +{ + hide_ap = false; + add_scan_results(); +} + + +void Peers::ctx_p2p_show_passphrase() +{ + char reply[64]; + size_t reply_len; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to get P2P group passphrase."); + msg.exec(); + } else { + reply[reply_len] = '\0'; + QMessageBox::information(this, tr("Passphrase"), + tr("P2P group passphrase:\n") + + reply); + } +} + + +void Peers::ctx_p2p_start_persistent() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start persistent P2P Group.")); + msg.exec(); + } else if (ctx_item->data(peer_role_type).toInt() == + PEER_TYPE_P2P_INVITATION) + model.removeRow(ctx_item->row()); +} + + +void Peers::ctx_p2p_invite() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to invite peer to start persistent " + "P2P Group.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_delete() +{ + if (ctx_item == NULL) + return; + model.removeRow(ctx_item->row()); +} + + +void Peers::enable_persistent(int id) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_network_id, id); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) + item->setBackground(Qt::NoBrush); + } +} diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h index 89b7b5b..a715395 100644 --- a/wpa_supplicant/wpa_gui-qt4/peers.h +++ b/wpa_supplicant/wpa_gui-qt4/peers.h @@ -1,6 +1,6 @@ /* * wpa_gui - Peers class - * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2009-2010, 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 @@ -39,7 +39,24 @@ public slots: virtual void connect_pbc(); virtual void learn_ap_config(); virtual void ctx_refresh(); + virtual void ctx_p2p_start(); + virtual void ctx_p2p_stop(); + virtual void ctx_p2p_listen(); + virtual void ctx_p2p_start_group(); + virtual void ctx_p2p_remove_group(); + virtual void ctx_p2p_connect(); + virtual void ctx_p2p_req_pin(); + virtual void ctx_p2p_show_pin(); + virtual void ctx_p2p_display_pin(); + virtual void ctx_p2p_display_pin_pd(); + virtual void ctx_p2p_enter_pin(); virtual void properties(); + virtual void ctx_hide_ap(); + virtual void ctx_show_ap(); + virtual void ctx_p2p_show_passphrase(); + virtual void ctx_p2p_start_persistent(); + virtual void ctx_p2p_invite(); + virtual void ctx_p2p_delete(); protected slots: virtual void languageChange(); @@ -52,19 +69,28 @@ private: bool add_bss(const char *cmd); void remove_bss(int id); void add_scan_results(); + void add_persistent(int id, const char *ssid, const char *bssid); + void add_persistent_groups(); void update_peers(); QStandardItem * find_addr(QString addr); + QStandardItem * find_addr_type(QString addr, int type); + void add_p2p_group_client(QStandardItem *parent, QString params); QStandardItem * find_uuid(QString uuid); void done(int r); void remove_enrollee_uuid(QString uuid); QString ItemType(int type); + void enable_persistent(int id); WpaGui *wpagui; QStandardItemModel model; QIcon *default_icon; QIcon *ap_icon; QIcon *laptop_icon; + QIcon *group_icon; + QIcon *invitation_icon; QStandardItem *ctx_item; + + bool hide_ap; }; #endif /* PEERS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp index 2057d67..97bf5ac 100644 --- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -1,6 +1,6 @@ /* * wpa_gui - WpaGui class - * Copyright (c) 2005-2010, Jouni Malinen + * Copyright (c) 2005-2011, Jouni Malinen * * 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 @@ -659,6 +659,13 @@ void WpaGui::updateNetworks() break; *flags++ = '\0'; + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) { + if (last) + break; + start = end + 1; + continue; + } + QString network(id); network.append(": "); network.append(ssid); @@ -712,7 +719,7 @@ void WpaGui::helpContents() void WpaGui::helpAbout() { QMessageBox::about(this, "wpa_gui for wpa_supplicant", - "Copyright (c) 2003-2010,\n" + "Copyright (c) 2003-2011,\n" "Jouni Malinen \n" "and contributors.\n" "\n" @@ -899,6 +906,15 @@ void WpaGui::processMsg(char *msg) if (textStatus->text() == "INACTIVE" || textStatus->text() == "DISCONNECTED") wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "indicating this client is authorized."); + wpsStatusText->setText("WPS AP indicating this client is " + "authorized"); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) { wpsStatusText->setText(tr("WPS AP detected")); } else if (str_match(pos, WPS_EVENT_OVERLAP)) { diff --git a/wpa_supplicant/wpa_gui/.gitignore b/wpa_supplicant/wpa_gui/.gitignore index 11963c8..4df64a9 100644 --- a/wpa_supplicant/wpa_gui/.gitignore +++ b/wpa_supplicant/wpa_gui/.gitignore @@ -1,5 +1,3 @@ .moc .obj .ui -Makefile -wpa_gui diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index d2a991b..c8854da 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/wpa_supplicant/wpa_priv.c @@ -825,7 +825,7 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface, } -void wpa_supplicant_event(void *ctx, wpa_event_type event, +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_priv_interface *iface = ctx; @@ -915,7 +915,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, } -static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx) +static void wpa_priv_terminate(int sig, void *signal_ctx) { wpa_printf(MSG_DEBUG, "wpa_priv termination requested"); eloop_terminate(); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 37a539d..cddc694 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * * 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 @@ -19,6 +19,8 @@ #include "includes.h" #include "common.h" +#include "crypto/random.h" +#include "crypto/sha1.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" #include "eap_server/eap_methods.h" @@ -34,22 +36,25 @@ #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" -#include "mlme.h" #include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" +#include "gas_query.h" #include "ap.h" +#include "p2p_supplicant.h" #include "notify.h" #include "bgscan.h" #include "bss.h" #include "scan.h" +#include "offchannel.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2010, Jouni Malinen and contributors"; +"Copyright (c) 2003-2012, Jouni Malinen and contributors"; const char *wpa_supplicant_license = "This program is free software. You can distribute it and/or modify it\n" @@ -130,9 +135,8 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) continue; set = 1; - wpa_drv_set_key(wpa_s, WPA_ALG_WEP, - (u8 *) "\xff\xff\xff\xff\xff\xff", - i, i == ssid->wep_tx_keyidx, (u8 *) "", 0, + wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL, + i, i == ssid->wep_tx_keyidx, NULL, 0, ssid->wep_key[i], ssid->wep_key_len[i]); } @@ -152,13 +156,14 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, * sending unicast and multicast packets. */ if (ssid->mode != WPAS_MODE_IBSS) { - wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) " - "for WPA-None", ssid->mode); + wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not " + "IBSS/ad-hoc) for WPA-None", ssid->mode); return -1; } if (!ssid->psk_set) { - wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None"); + wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for " + "WPA-None"); return -1; } @@ -176,16 +181,15 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, alg = WPA_ALG_TKIP; break; default: - wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for " - "WPA-None", wpa_s->group_cipher); + wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for " + "WPA-None", wpa_s->group_cipher); return -1; } /* TODO: should actually remember the previously used seq#, both for TX * and RX from each STA.. */ - return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", - 0, 1, seq, 6, key, keylen); + return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); } @@ -201,7 +205,12 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) wpa_sm_notify_disassoc(wpa_s->wpa); wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + /* + * If we timed out, the AP or the local radio may be busy. + * So, wait a second until scanning again. + */ + wpa_supplicant_req_scan(wpa_s, 1, 0); } @@ -221,7 +230,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) return; - wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " + wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " "%d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); @@ -238,7 +247,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, */ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) { - wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); wpa_blacklist_del(wpa_s, wpa_s->bssid); } @@ -361,6 +370,22 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, } +void free_hw_features(struct wpa_supplicant *wpa_s) +{ + int i; + if (wpa_s->hw.modes == NULL) + return; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + os_free(wpa_s->hw.modes[i].channels); + os_free(wpa_s->hw.modes[i].rates); + } + + os_free(wpa_s->hw.modes); + wpa_s->hw.modes = NULL; +} + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { bgscan_deinit(wpa_s); @@ -396,6 +421,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) rsn_preauth_deinit(wpa_s->wpa); +#ifdef CONFIG_TDLS + wpa_tdls_deinit(wpa_s->wpa); +#endif /* CONFIG_TDLS */ + pmksa_candidate_free(wpa_s->wpa); wpa_sm_deinit(wpa_s->wpa); wpa_s->wpa = NULL; @@ -405,8 +434,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_cancel_auth_timeout(wpa_s); - - ieee80211_sta_deinit(wpa_s); + eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT + eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ wpas_wps_deinit(wpa_s); @@ -418,15 +450,29 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->ibss_rsn = NULL; #endif /* CONFIG_IBSS_RSN */ -#ifdef CONFIG_SME - os_free(wpa_s->sme.ft_ies); - wpa_s->sme.ft_ies = NULL; - wpa_s->sme.ft_ies_len = 0; -#endif /* CONFIG_SME */ + sme_deinit(wpa_s); #ifdef CONFIG_AP wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ + +#ifdef CONFIG_P2P + wpas_p2p_deinit(wpa_s); +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_OFFCHANNEL + offchannel_deinit(wpa_s); +#endif /* CONFIG_OFFCHANNEL */ + + wpa_supplicant_cancel_sched_scan(wpa_s); + + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; + + gas_query_deinit(wpa_s->gas); + wpa_s->gas = NULL; + + free_hw_features(wpa_s); } @@ -440,8 +486,6 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) */ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { - u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; - if (wpa_s->keys_cleared) { /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have * timing issues with keys being cleared just before new keys @@ -450,19 +494,19 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) * client not receiving the first encrypted packets correctly. * Skipping some of the extra key clearing steps seems to help * in completing group key handshake more reliably. */ - wpa_printf(MSG_DEBUG, "No keys have been configured - " - "skip key clearing"); + wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - " + "skip key clearing"); return; } /* MLME-DELETEKEYS.request */ - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0); #ifdef CONFIG_IEEE80211W - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0); #endif /* CONFIG_IEEE80211W */ if (addr) { wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, @@ -489,6 +533,8 @@ const char * wpa_supplicant_state_txt(enum wpa_states state) return "DISCONNECTED"; case WPA_INACTIVE: return "INACTIVE"; + case WPA_INTERFACE_DISABLED: + return "INTERFACE_DISABLED"; case WPA_SCANNING: return "SCANNING"; case WPA_AUTHENTICATING: @@ -509,6 +555,43 @@ const char * wpa_supplicant_state_txt(enum wpa_states state) } +#ifdef CONFIG_BGSCAN + +static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpas_driver_bss_selection(wpa_s)) + return; + if (wpa_s->current_ssid == wpa_s->bgscan_ssid) + return; + + bgscan_deinit(wpa_s); + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { + if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " + "bgscan"); + /* + * Live without bgscan; it is only used as a roaming + * optimization, so the initial connection is not + * affected. + */ + } else + wpa_s->bgscan_ssid = wpa_s->current_ssid; + } else + wpa_s->bgscan_ssid = NULL; +} + + +static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan_ssid != NULL) { + bgscan_deinit(wpa_s); + wpa_s->bgscan_ssid = NULL; + } +} + +#endif /* CONFIG_BGSCAN */ + + /** * wpa_supplicant_set_state - Set current connection state * @wpa_s: Pointer to wpa_supplicant data @@ -522,9 +605,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, { enum wpa_states old_state = wpa_s->wpa_state; - wpa_printf(MSG_DEBUG, "State: %s -> %s", - wpa_supplicant_state_txt(wpa_s->wpa_state), - wpa_supplicant_state_txt(state)); + wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s", + wpa_supplicant_state_txt(wpa_s->wpa_state), + wpa_supplicant_state_txt(state)); if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); @@ -542,16 +625,37 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_s->new_connection = 0; wpa_s->reassociated_connection = 1; wpa_drv_set_operstate(wpa_s, 1); +#ifndef IEEE8021X_EAPOL + wpa_drv_set_supp_port(wpa_s, 1); +#endif /* IEEE8021X_EAPOL */ wpa_s->after_wps = 0; +#ifdef CONFIG_P2P + wpas_p2p_completed(wpa_s); +#endif /* CONFIG_P2P */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; wpa_drv_set_operstate(wpa_s, 0); +#ifndef IEEE8021X_EAPOL + wpa_drv_set_supp_port(wpa_s, 0); +#endif /* IEEE8021X_EAPOL */ } wpa_s->wpa_state = state; - if (wpa_s->wpa_state != old_state) +#ifdef CONFIG_BGSCAN + if (state == WPA_COMPLETED) + wpa_supplicant_start_bgscan(wpa_s); + else + wpa_supplicant_stop_bgscan(wpa_s); +#endif /* CONFIG_BGSCAN */ + + if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); + + if (wpa_s->wpa_state == WPA_COMPLETED || + old_state == WPA_COMPLETED) + wpas_notify_auth_changed(wpa_s); + } } @@ -584,7 +688,7 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx) } -static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) +void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) { enum wpa_states old_state = wpa_s->wpa_state; @@ -592,7 +696,8 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) wpa_s->group_cipher = 0; wpa_s->mgmt_group_cipher = 0; wpa_s->key_mgmt = 0; - wpa_s->wpa_state = WPA_DISCONNECTED; + if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) + wpa_s->wpa_state = WPA_DISCONNECTED; if (wpa_s->wpa_state != old_state) wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -613,7 +718,6 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) { struct wpa_config *conf; - struct wpa_ssid *old_ssid; int reconf_ctrl; int old_ap_scan; @@ -625,6 +729,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) "file '%s' - exiting", wpa_s->confname); return -1; } + conf->changed_parameters = (unsigned int) -1; reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && @@ -637,10 +742,10 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) } eapol_sm_invalidate_cached_session(wpa_s->eapol); - old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = NULL; - if (old_ssid != wpa_s->current_ssid) - wpas_notify_network_changed(wpa_s); + if (wpa_s->current_ssid) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } /* * TODO: should notify EAPOL SM about changes in opensc_engine_path, @@ -655,6 +760,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) } eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_sm_set_config(wpa_s->wpa, NULL); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth); rsn_preauth_deinit(wpa_s->wpa); @@ -667,10 +773,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) if (reconf_ctrl) wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); + wpa_supplicant_update_config(wpa_s); + wpa_supplicant_clear_status(wpa_s); - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); - wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); + if (wpa_supplicant_enabled_networks(wpa_s->conf)) { + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); return 0; } @@ -679,8 +789,9 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx) { struct wpa_global *global = signal_ctx; struct wpa_supplicant *wpa_s; - wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig); for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring", + sig); if (wpa_supplicant_reload_configuration(wpa_s) < 0) { wpa_supplicant_terminate_proc(global); } @@ -747,8 +858,8 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, return -1; } - wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher " - "suites"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set " + "cipher suites"); if (!(ie->group_cipher & ssid->group_cipher)) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " "cipher 0x%x (mask 0x%x) - reject", @@ -815,14 +926,14 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { - wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); proto = WPA_PROTO_RSN; } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) && wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 && (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); proto = WPA_PROTO_WPA; } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); @@ -842,22 +953,23 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ? WPA_CIPHER_AES_128_CMAC : 0; #endif /* CONFIG_IEEE80211W */ - wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based " - "on configuration"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " + "based on configuration"); } else proto = ie.proto; } - wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d " - "pairwise %d key_mgmt %d proto %d", - ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d " + "pairwise %d key_mgmt %d proto %d", + ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); #ifdef CONFIG_IEEE80211W if (ssid->ieee80211w) { - wpa_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d", - ie.mgmt_group_cipher); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d", + ie.mgmt_group_cipher); } #endif /* CONFIG_IEEE80211W */ + wpa_s->wpa_proto = proto; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, !!(ssid->proto & WPA_PROTO_RSN)); @@ -873,34 +985,35 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, sel = ie.group_cipher & ssid->group_cipher; if (sel & WPA_CIPHER_CCMP) { wpa_s->group_cipher = WPA_CIPHER_CCMP; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); } else if (sel & WPA_CIPHER_TKIP) { wpa_s->group_cipher = WPA_CIPHER_TKIP; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); } else if (sel & WPA_CIPHER_WEP104) { wpa_s->group_cipher = WPA_CIPHER_WEP104; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); } else if (sel & WPA_CIPHER_WEP40) { wpa_s->group_cipher = WPA_CIPHER_WEP40; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); } else { - wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher."); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " + "cipher"); return -1; } sel = ie.pairwise_cipher & ssid->pairwise_cipher; if (sel & WPA_CIPHER_CCMP) { wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); } else if (sel & WPA_CIPHER_TKIP) { wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); } else if (sel & WPA_CIPHER_NONE) { wpa_s->pairwise_cipher = WPA_CIPHER_NONE; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); } else { - wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " - "cipher."); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " + "cipher"); return -1; } @@ -909,33 +1022,33 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); } else if (sel & WPA_KEY_MGMT_FT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; - wpa_msg(wpa_s, MSG_DEBUG, + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with SHA256"); } else if (sel & WPA_KEY_MGMT_PSK_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; - wpa_msg(wpa_s, MSG_DEBUG, + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT PSK with SHA256"); #endif /* CONFIG_IEEE80211W */ } else if (sel & WPA_KEY_MGMT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); } else if (sel & WPA_KEY_MGMT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); } else if (sel & WPA_KEY_MGMT_WPA_NONE) { wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); } else { - wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated " - "key management type."); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " + "authenticated key management type"); return -1; } @@ -951,11 +1064,11 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, sel = 0; if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "AES-128-CMAC"); } else { wpa_s->mgmt_group_cipher = 0; - wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); } wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); @@ -963,14 +1076,26 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211W */ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { - wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE."); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; } if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) + { wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); - else +#ifndef CONFIG_NO_PBKDF2 + if (bss && ssid->bssid_set && ssid->ssid_len == 0 && + ssid->passphrase) { + u8 psk[PMK_LEN]; + pbkdf2_sha1(ssid->passphrase, (char *) bss->ssid, + bss->ssid_len, 4096, psk, PMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", + psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } +#endif /* CONFIG_NO_PBKDF2 */ + } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); return 0; @@ -988,7 +1113,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { - u8 wpa_ie[80]; + u8 wpa_ie[200]; size_t wpa_ie_len; int use_crypt, ret, i, bssid_changed; int algs = WPA_AUTH_ALG_OPEN; @@ -999,30 +1124,43 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, int assoc_failed = 0; struct wpa_ssid *old_ssid; - if (ssid->mode == WPAS_MODE_AP) { +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + + if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { #ifdef CONFIG_AP if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) { - wpa_printf(MSG_INFO, "Driver does not support AP " - "mode"); + wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP " + "mode"); return; } wpa_supplicant_create_ap(wpa_s, ssid); wpa_s->current_bss = bss; #else /* CONFIG_AP */ - wpa_printf(MSG_ERROR, "AP mode support not included in the " - "build"); + wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in " + "the build"); #endif /* CONFIG_AP */ return; } +#ifdef CONFIG_TDLS + if (bss) + wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), + bss->ie_len); +#endif /* CONFIG_TDLS */ + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && ssid->mode == IEEE80211_MODE_INFRA) { sme_authenticate(wpa_s, bss, ssid); return; } + os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; - if (bss) { + if (bss && !wpas_driver_bss_selection(wpa_s)) { #ifdef CONFIG_IEEE80211R const u8 *ie, *md = NULL; #endif /* CONFIG_IEEE80211R */ @@ -1060,6 +1198,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); /* Starting new association, so clear the possibly used WPA IE from the @@ -1076,11 +1215,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } } #endif /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); if (ssid->auth_alg) { algs = ssid->auth_alg; - wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", - algs); + wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " + "0x%x", algs); } if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || @@ -1100,8 +1239,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { - wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " - "management and encryption suites"); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " + "key management and encryption suites"); return; } } else if (ssid->key_mgmt & @@ -1112,9 +1251,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { - wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " - "management and encryption suites (no scan " - "results)"); + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " + "key management and encryption suites (no " + "scan results)"); return; } #ifdef CONFIG_WPS @@ -1128,12 +1267,63 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_ie_len = 0; wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY)) + params.wps = WPS_MODE_PRIVACY; + else + params.wps = WPS_MODE_OPEN; + wpa_s->wpa_proto = 0; #endif /* CONFIG_WPS */ } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_ie_len = 0; + wpa_s->wpa_proto = 0; } +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + u8 *pos; + size_t len; + int res; + pos = wpa_ie + wpa_ie_len; + len = sizeof(wpa_ie) - wpa_ie_len; + res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, + ssid->p2p_group); + if (res >= 0) + wpa_ie_len += res; + } + + wpa_s->cross_connect_disallowed = 0; + if (bss) { + struct wpabuf *p2p; + p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p) { + wpa_s->cross_connect_disallowed = + p2p_get_cross_connect_disallowed(p2p); + wpabuf_free(p2p); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross " + "connection", + wpa_s->cross_connect_disallowed ? + "disallows" : "allows"); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking) { + u8 *pos = wpa_ie; + if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + 6, pos, wpa_ie_len - (pos - wpa_ie)); + wpa_ie_len += 6; + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = 4; + *pos++ = 0x00; + *pos++ = 0x00; + *pos++ = 0x00; + *pos++ = 0x80; /* Bit 31 - Interworking */ + } +#endif /* CONFIG_INTERWORKING */ + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); @@ -1172,12 +1362,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); - os_memset(¶ms, 0, sizeof(params)); if (bss) { - params.bssid = bss->bssid; params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; - params.freq = bss->freq; + if (!wpas_driver_bss_selection(wpa_s)) { + params.bssid = bss->bssid; + params.freq = bss->freq; + } } else { params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; @@ -1190,6 +1381,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.wpa_proto = wpa_s->wpa_proto; params.auth_alg = algs; params.mode = ssid->mode; for (i = 0; i < NUM_WEP_KEYS; i++) { @@ -1217,21 +1409,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { - wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " - "require MFP"); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports " + "MFP: require MFP"); params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_REQUIRED; } } #endif /* CONFIG_IEEE80211W */ - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - ret = ieee80211_sta_associate(wpa_s, ¶ms); + params.p2p = ssid->p2p_group; + + if (wpa_s->parent->set_sta_uapsd) + params.uapsd = wpa_s->parent->sta_uapsd; else - ret = wpa_drv_associate(wpa_s, ¶ms); + params.uapsd = -1; + + ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { + /* + * The driver is known to mean what is saying, so we + * can stop right here; the association will not + * succeed. + */ + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + return; + } /* try to continue anyway; new association will be tried again * after timeout */ assoc_failed = 1; @@ -1249,7 +1455,6 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } else if (ssid->mode == WPAS_MODE_IBSS && wpa_s->key_mgmt != WPA_KEY_MGMT_NONE && wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) { - ibss_rsn_set_psk(wpa_s->ibss_rsn, ssid->psk); /* * RSN IBSS authentication is per-STA and we can disable the * per-BSSID authentication. @@ -1293,6 +1498,24 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } +static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + struct wpa_ssid *old_ssid; + + wpa_clear_keys(wpa_s, addr); + wpa_supplicant_mark_disassoc(wpa_s); + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = NULL; + wpa_s->current_bss = NULL; + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + if (old_ssid != wpa_s->current_ssid) + wpas_notify_network_changed(wpa_s); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); +} + + /** * wpa_supplicant_disassociate - Disassociate the current connection * @wpa_s: Pointer to wpa_supplicant data @@ -1304,26 +1527,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, int reason_code) { - struct wpa_ssid *old_ssid; u8 *addr = NULL; if (!is_zero_ether_addr(wpa_s->bssid)) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - ieee80211_sta_disassociate(wpa_s, reason_code); - else - wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); + wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); addr = wpa_s->bssid; } - wpa_clear_keys(wpa_s, addr); - wpa_supplicant_mark_disassoc(wpa_s); - old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = NULL; - wpa_s->current_bss = NULL; - wpa_sm_set_config(wpa_s->wpa, NULL); - eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - if (old_ssid != wpa_s->current_ssid) - wpas_notify_network_changed(wpa_s); - eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + + wpa_supplicant_clear_connection(wpa_s, addr); } @@ -1338,27 +1549,14 @@ void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code) { - struct wpa_ssid *old_ssid; u8 *addr = NULL; if (!is_zero_ether_addr(wpa_s->bssid)) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - ieee80211_sta_deauthenticate(wpa_s, reason_code); - else - wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, - reason_code); + wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code); addr = wpa_s->bssid; } - wpa_clear_keys(wpa_s, addr); - wpa_supplicant_mark_disassoc(wpa_s); - old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = NULL; - wpa_s->current_bss = NULL; - wpa_sm_set_config(wpa_s->wpa, NULL); - eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - if (old_ssid != wpa_s->current_ssid) - wpas_notify_network_changed(wpa_s); - eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + + wpa_supplicant_clear_connection(wpa_s, addr); } @@ -1376,8 +1574,11 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, int was_disabled; if (ssid == NULL) { - other_ssid = wpa_s->conf->ssid; - while (other_ssid) { + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { + if (other_ssid->disabled == 2) + continue; /* do not change persistent P2P group + * data */ if (other_ssid == wpa_s->current_ssid && other_ssid->disabled) wpa_s->reassociate = 1; @@ -1389,12 +1590,10 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( wpa_s, other_ssid); - - other_ssid = other_ssid->next; } if (wpa_s->reassociate) wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (ssid->disabled) { + } else if (ssid->disabled && ssid->disabled != 2) { if (wpa_s->current_ssid == NULL) { /* * Try to reassociate since there is no current @@ -1428,22 +1627,23 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, int was_disabled; if (ssid == NULL) { - other_ssid = wpa_s->conf->ssid; - while (other_ssid) { + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { was_disabled = other_ssid->disabled; + if (was_disabled == 2) + continue; /* do not change persistent P2P group + * data */ other_ssid->disabled = 1; if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( wpa_s, other_ssid); - - other_ssid = other_ssid->next; } if (wpa_s->current_ssid) wpa_supplicant_disassociate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); - } else { + } else if (ssid->disabled != 2) { if (ssid == wpa_s->current_ssid) wpa_supplicant_disassociate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); @@ -1468,29 +1668,41 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, { struct wpa_ssid *other_ssid; + int disconnected = 0; - if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) + if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { wpa_supplicant_disassociate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + disconnected = 1; + } /* * Mark all other networks disabled or mark all networks enabled if no * network specified. */ - other_ssid = wpa_s->conf->ssid; - while (other_ssid) { + for (other_ssid = wpa_s->conf->ssid; other_ssid; + other_ssid = other_ssid->next) { int was_disabled = other_ssid->disabled; + if (was_disabled == 2) + continue; /* do not change persistent P2P group data */ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, other_ssid); + } - other_ssid = other_ssid->next; + if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) { + /* We are already associated with the selected network */ + wpa_printf(MSG_DEBUG, "Already associated with the " + "selected network - do nothing"); + return; } + + wpa_s->connect_without_scan = NULL; wpa_s->disconnected = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); if (ssid) wpas_notify_network_selected(wpa_s, ssid); @@ -1512,6 +1724,16 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) if (ap_scan < 0 || ap_scan > 2) return -1; +#ifdef ANDROID + if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan && + wpa_s->wpa_state >= WPA_ASSOCIATING && + wpa_s->wpa_state < WPA_COMPLETED) { + wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while " + "associating", wpa_s->conf->ap_scan, ap_scan); + return 0; + } +#endif /* ANDROID */ + old_ap_scan = wpa_s->conf->ap_scan; wpa_s->conf->ap_scan = ap_scan; @@ -1523,6 +1745,52 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan) /** + * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age + * @wpa_s: wpa_supplicant structure for a network interface + * @expire_age: Expiration age in seconds + * Returns: 0 if succeed or -1 if expire_age has an invalid value + * + */ +int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, + unsigned int bss_expire_age) +{ + if (bss_expire_age < 10) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u", + bss_expire_age); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec", + bss_expire_age); + wpa_s->conf->bss_expiration_age = bss_expire_age; + + return 0; +} + + +/** + * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count + * @wpa_s: wpa_supplicant structure for a network interface + * @expire_count: number of scans after which an unseen BSS is reclaimed + * Returns: 0 if succeed or -1 if expire_count has an invalid value + * + */ +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, + unsigned int bss_expire_count) +{ + if (bss_expire_count < 1) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u", + bss_expire_count); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u", + bss_expire_count); + wpa_s->conf->bss_expiration_scan_count = bss_expire_count; + + return 0; +} + + +/** * wpa_supplicant_set_debug_params - Set global debug params * @global: wpa_global structure * @debug_level: debug level @@ -1537,7 +1805,8 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int old_level, old_timestamp, old_show_keys; /* check for allowed debuglevels */ - if (debug_level != MSG_MSGDUMP && + if (debug_level != MSG_EXCESSIVE && + debug_level != MSG_MSGDUMP && debug_level != MSG_DEBUG && debug_level != MSG_INFO && debug_level != MSG_WARNING && @@ -1577,26 +1846,17 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) u8 bssid[ETH_ALEN]; int wired; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { - if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) { - wpa_printf(MSG_WARNING, "Could not read SSID from " - "MLME."); - return NULL; - } - } else { - res = wpa_drv_get_ssid(wpa_s, ssid); - if (res < 0) { - wpa_printf(MSG_WARNING, "Could not read SSID from " - "driver."); - return NULL; - } - ssid_len = res; + res = wpa_drv_get_ssid(wpa_s, ssid); + if (res < 0) { + wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from " + "driver"); + return NULL; } + ssid_len = res; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); - else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { - wpa_printf(MSG_WARNING, "Could not read BSSID from driver."); + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from " + "driver"); return NULL; } @@ -1619,6 +1879,12 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #endif /* CONFIG_WPS */ + + if (!entry->disabled && entry->bssid_set && + entry->ssid_len == 0 && + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) + return entry; + entry = entry->next; } @@ -1626,45 +1892,68 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) } +static int select_driver(struct wpa_supplicant *wpa_s, int i) +{ + struct wpa_global *global = wpa_s->global; + + if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) { + global->drv_priv[i] = wpa_drivers[i]->global_init(); + if (global->drv_priv[i] == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize driver " + "'%s'", wpa_drivers[i]->name); + return -1; + } + } + + wpa_s->driver = wpa_drivers[i]; + wpa_s->global_drv_priv = global->drv_priv[i]; + + return 0; +} + + static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, const char *name) { int i; size_t len; - const char *pos; + const char *pos, *driver = name; if (wpa_s == NULL) return -1; if (wpa_drivers[0] == NULL) { - wpa_printf(MSG_ERROR, "No driver interfaces build into " - "wpa_supplicant."); + wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into " + "wpa_supplicant"); return -1; } if (name == NULL) { /* default to first driver in the list */ - wpa_s->driver = wpa_drivers[0]; - wpa_s->global_drv_priv = wpa_s->global->drv_priv[0]; - return 0; + return select_driver(wpa_s, 0); } - pos = os_strchr(name, ','); - if (pos) - len = pos - name; - else - len = os_strlen(name); - for (i = 0; wpa_drivers[i]; i++) { - if (os_strlen(wpa_drivers[i]->name) == len && - os_strncmp(name, wpa_drivers[i]->name, len) == - 0) { - wpa_s->driver = wpa_drivers[i]; - wpa_s->global_drv_priv = wpa_s->global->drv_priv[i]; - return 0; + do { + pos = os_strchr(driver, ','); + if (pos) + len = pos - driver; + else + len = os_strlen(driver); + + for (i = 0; wpa_drivers[i]; i++) { + if (os_strlen(wpa_drivers[i]->name) == len && + os_strncmp(driver, wpa_drivers[i]->name, len) == + 0) { + /* First driver that succeeds wins */ + if (select_driver(wpa_s, i) == 0) + return 0; + } } - } - wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name); + driver = pos + 1; + } while (pos); + + wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name); return -1; } @@ -1688,7 +1977,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, { struct wpa_supplicant *wpa_s = ctx; - wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); if (wpa_s->wpa_state < WPA_ASSOCIATED) { @@ -1700,8 +1989,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, * association information, lets queue it for processing until * the association event is received. */ - wpa_printf(MSG_DEBUG, "Not associated - Delay processing of " - "received EAPOL frame"); + wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing " + "of received EAPOL frame"); wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { @@ -1720,8 +2009,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, #endif /* CONFIG_AP */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { - wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since " - "no key management is configured"); + wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since " + "no key management is configured"); return; } @@ -1742,8 +2031,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_s->eapol_received++; if (wpa_s->countermeasures) { - wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL " - "packet"); + wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped " + "EAPOL packet"); return; } @@ -1780,52 +2069,68 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, } -/** - * wpa_supplicant_driver_init - Initialize driver interface parameters - * @wpa_s: Pointer to wpa_supplicant data - * Returns: 0 on success, -1 on failure - * - * This function is called to initialize driver interface parameters. - * wpa_drv_init() must have been called before this function to initialize the - * driver interface. - */ -int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) { - static int interface_count = 0; - if (wpa_s->driver->send_eapol) { const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); - } else { + } else if (!(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { + l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), ETH_P_EAPOL, wpa_supplicant_rx_eapol, wpa_s, 0); if (wpa_s->l2 == NULL) return -1; + } else { + const u8 *addr = wpa_drv_get_mac_addr(wpa_s); + if (addr) + os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); } if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { - wpa_printf(MSG_ERROR, "Failed to get own L2 address"); + wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address"); return -1; } - wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR, - MAC2STR(wpa_s->own_addr)); + wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + + return 0; +} + + +/** + * wpa_supplicant_driver_init - Initialize driver interface parameters + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +{ + static int interface_count = 0; + + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) + return -1; if (wpa_s->bridge_ifname[0]) { - wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface" - " '%s'", wpa_s->bridge_ifname); + wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " + "interface '%s'", wpa_s->bridge_ifname); wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname, wpa_s->own_addr, ETH_P_EAPOL, wpa_supplicant_rx_eapol, wpa_s, 0); if (wpa_s->l2_br == NULL) { - wpa_printf(MSG_ERROR, "Failed to open l2_packet " - "connection for the bridge interface '%s'", - wpa_s->bridge_ifname); + wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " + "connection for the bridge interface '%s'", + wpa_s->bridge_ifname); return -1; } } @@ -1836,12 +2141,15 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) * happen if wpa_supplicant is killed during countermeasures. */ wpa_drv_set_countermeasures(wpa_s, 0); - wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver"); + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver"); wpa_drv_flush_pmkid(wpa_s); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; if (wpa_supplicant_enabled_networks(wpa_s->conf)) { - wpa_supplicant_req_scan(wpa_s, interface_count, 100000); + if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count, + 100000)) + wpa_supplicant_req_scan(wpa_s, interface_count, + 100000); interface_count++; } else wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); @@ -1865,7 +2173,10 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void) if (wpa_s == NULL) return NULL; wpa_s->scan_req = 1; + wpa_s->scan_interval = 5; wpa_s->new_connection = 1; + wpa_s->parent = wpa_s; + wpa_s->sched_scanning = 0; return wpa_s; } @@ -1969,24 +2280,25 @@ next_driver: const char *pos; pos = driver ? os_strchr(driver, ',') : NULL; if (pos) { - wpa_printf(MSG_DEBUG, "Failed to initialize driver " - "interface - try next driver wrapper"); + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " + "driver interface - try next driver wrapper"); driver = pos + 1; goto next_driver; } - wpa_printf(MSG_ERROR, "Failed to initialize driver interface"); + wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver " + "interface"); return -1; } if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { - wpa_printf(MSG_ERROR, "Driver interface rejected " - "driver_param '%s'", wpa_s->conf->driver_param); + wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected " + "driver_param '%s'", wpa_s->conf->driver_param); return -1; } ifname = wpa_drv_get_ifname(wpa_s); if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) { - wpa_printf(MSG_DEBUG, "Driver interface replaced interface " - "name with '%s'", ifname); + wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " + "interface name with '%s'", ifname); os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); } @@ -2001,15 +2313,15 @@ next_driver: if (wpa_s->conf->dot11RSNAConfigPMKLifetime && wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, wpa_s->conf->dot11RSNAConfigPMKLifetime)) { - wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " - "dot11RSNAConfigPMKLifetime"); + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigPMKLifetime"); return -1; } if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold && wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) { - wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " "dot11RSNAConfigPMKReauthThreshold"); return -1; } @@ -2017,19 +2329,24 @@ next_driver: if (wpa_s->conf->dot11RSNAConfigSATimeout && wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, wpa_s->conf->dot11RSNAConfigSATimeout)) { - wpa_printf(MSG_ERROR, "Invalid WPA parameter value for " - "dot11RSNAConfigSATimeout"); + wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for " + "dot11RSNAConfigSATimeout"); return -1; } + wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, + &wpa_s->hw.num_modes, + &wpa_s->hw.flags); + if (wpa_drv_get_capa(wpa_s, &capa) == 0) { + wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; - if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { - if (ieee80211_sta_init(wpa_s)) - return -1; - } wpa_s->max_scan_ssids = capa.max_scan_ssids; + wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; + wpa_s->sched_scan_supported = capa.sched_scan_supported; + wpa_s->max_match_sets = capa.max_match_sets; wpa_s->max_remain_on_chan = capa.max_remain_on_chan; + wpa_s->max_stations = capa.max_stations; } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; @@ -2037,14 +2354,17 @@ next_driver: if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; +#ifdef CONFIG_TDLS + if (wpa_tdls_init(wpa_s->wpa)) + return -1; +#endif /* CONFIG_TDLS */ + if (wpa_s->conf->country[0] && wpa_s->conf->country[1] && wpa_drv_set_country(wpa_s, wpa_s->conf->country)) { - wpa_printf(MSG_DEBUG, "Failed to set country"); + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country"); return -1; } - wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); - if (wpas_wps_init(wpa_s)) return -1; @@ -2066,13 +2386,18 @@ next_driver: return -1; } -#ifdef CONFIG_IBSS_RSN - wpa_s->ibss_rsn = ibss_rsn_init(wpa_s); - if (!wpa_s->ibss_rsn) { - wpa_printf(MSG_DEBUG, "Failed to init IBSS RSN"); + wpa_s->gas = gas_query_init(wpa_s); + if (wpa_s->gas == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize GAS query"); return -1; } -#endif /* CONFIG_IBSS_RSN */ + +#ifdef CONFIG_P2P + if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); + return -1; + } +#endif /* CONFIG_P2P */ if (wpa_bss_init(wpa_s) < 0) return -1; @@ -2166,7 +2491,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, wpa_s->next = global->ifaces; global->ifaces = wpa_s; - wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname); + wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); return wpa_s; } @@ -2200,8 +2525,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, prev->next = wpa_s->next; } - wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname); + wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); + if (global->p2p_group_formation == wpa_s) + global->p2p_group_formation = NULL; wpa_supplicant_deinit_iface(wpa_s, 1); os_free(wpa_s); @@ -2210,6 +2537,28 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, /** + * wpa_supplicant_get_eap_mode - Get the current EAP mode + * @wpa_s: Pointer to the network interface + * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found + */ +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s) +{ + const char *eapol_method; + + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 && + wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + return "NO-EAP"; + } + + eapol_method = eapol_sm_get_method_name(wpa_s->eapol); + if (eapol_method == NULL) + return "UNKNOWN-EAP"; + + return eapol_method; +} + + +/** * wpa_supplicant_get_iface - Get a new network interface * @global: Pointer to global data from wpa_supplicant_init() * @ifname: Interface name @@ -2228,6 +2577,17 @@ struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, } +#ifndef CONFIG_NO_WPA_MSG +static const char * wpa_supplicant_msg_ifname_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL) + return NULL; + return wpa_s->ifname; +} +#endif /* CONFIG_NO_WPA_MSG */ + + /** * wpa_supplicant_init - Initialize %wpa_supplicant * @params: Parameters for %wpa_supplicant @@ -2245,6 +2605,17 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params == NULL) return NULL; +#ifdef CONFIG_DRIVER_NDIS + { + void driver_ndis_init_ops(void); + driver_ndis_init_ops(); + } +#endif /* CONFIG_DRIVER_NDIS */ + +#ifndef CONFIG_NO_WPA_MSG + wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb); +#endif /* CONFIG_NO_WPA_MSG */ + wpa_debug_open_file(params->wpa_debug_file_path); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); @@ -2261,6 +2632,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + dl_list_init(&global->p2p_srv_bonjour); + dl_list_init(&global->p2p_srv_upnp); global->params.daemonize = params->daemonize; global->params.wait_for_monitor = params->wait_for_monitor; global->params.dbus_ctrl_interface = params->dbus_ctrl_interface; @@ -2282,12 +2655,16 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_debug_timestamp = global->params.wpa_debug_timestamp = params->wpa_debug_timestamp; + wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR); + if (eloop_init()) { wpa_printf(MSG_ERROR, "Failed to initialize event loop"); wpa_supplicant_deinit(global); return NULL; } + random_init(params->entropy_file); + global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); if (global->ctrl_iface == NULL) { wpa_supplicant_deinit(global); @@ -2311,17 +2688,6 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_supplicant_deinit(global); return NULL; } - for (i = 0; wpa_drivers[i]; i++) { - if (!wpa_drivers[i]->global_init) - continue; - global->drv_priv[i] = wpa_drivers[i]->global_init(); - if (global->drv_priv[i] == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize driver " - "'%s'", wpa_drivers[i]->name); - wpa_supplicant_deinit(global); - return NULL; - } - } return global; } @@ -2374,6 +2740,10 @@ void wpa_supplicant_deinit(struct wpa_global *global) if (global == NULL) return; +#ifdef CONFIG_P2P + wpas_p2p_deinit_global(global); +#endif /* CONFIG_P2P */ + while (global->ifaces) wpa_supplicant_remove_iface(global, global->ifaces); @@ -2394,6 +2764,8 @@ void wpa_supplicant_deinit(struct wpa_global *global) } os_free(global->drv_priv); + random_deinit(); + eloop_destroy(); if (global->params.pid_file) { @@ -2408,3 +2780,149 @@ void wpa_supplicant_deinit(struct wpa_global *global) wpa_debug_close_syslog(); wpa_debug_close_file(); } + + +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) +{ + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + if (wpa_drv_set_country(wpa_s, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code " + "'%s'", country); + } + } + +#ifdef CONFIG_WPS + wpas_wps_update_config(wpa_s); +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + wpas_p2p_update_config(wpa_s); +#endif /* CONFIG_P2P */ + + wpa_s->conf->changed_parameters = 0; +} + + +static void add_freq(int *freqs, int *num_freqs, int freq) +{ + int i; + + for (i = 0; i < *num_freqs; i++) { + if (freqs[i] == freq) + return; + } + + freqs[*num_freqs] = freq; + (*num_freqs)++; +} + + +static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *cbss; + const int max_freqs = 10; + int *freqs; + int num_freqs = 0; + + freqs = os_zalloc(sizeof(int) * (max_freqs + 1)); + if (freqs == NULL) + return NULL; + + cbss = wpa_s->current_bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == cbss) + continue; + if (bss->ssid_len == cbss->ssid_len && + os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && + wpa_blacklist_get(wpa_s, bss->bssid) == NULL) { + add_freq(freqs, &num_freqs, bss->freq); + if (num_freqs == max_freqs) + break; + } + } + + if (num_freqs == 0) { + os_free(freqs); + freqs = NULL; + } + + return freqs; +} + + +void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + int timeout; + int count; + int *freqs = NULL; + + /* + * Remove possible authentication timeout since the connection failed. + */ + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + + /* + * Add the failed BSSID into the blacklist and speed up next scan + * attempt if there could be other APs that could accept association. + * The current blacklist count indicates how many times we have tried + * connecting to this AP and multiple attempts mean that other APs are + * either not available or has already been tried, so that we can start + * increasing the delay here to avoid constant scanning. + */ + count = wpa_blacklist_add(wpa_s, bssid); + if (count == 1 && wpa_s->current_bss) { + /* + * This BSS was not in the blacklist before. If there is + * another BSS available for the same ESS, we should try that + * next. Otherwise, we may as well try this one once more + * before allowing other, likely worse, ESSes to be considered. + */ + freqs = get_bss_freqs_in_ess(wpa_s); + if (freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS " + "has been seen; try it next"); + wpa_blacklist_add(wpa_s, bssid); + /* + * On the next scan, go through only the known channels + * used in this ESS based on previous scans to speed up + * common load balancing use case. + */ + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = freqs; + } + } + + switch (count) { + case 1: + timeout = 100; + break; + case 2: + timeout = 500; + break; + case 3: + timeout = 1000; + break; + default: + timeout = 5000; + } + + /* + * TODO: if more than one possible AP is available in scan results, + * could try the other ones before requesting a new scan. + */ + wpa_supplicant_req_scan(wpa_s, timeout / 1000, + 1000 * (timeout % 1000)); +} + + +int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) +{ + return wpa_s->conf->ap_scan == 2 || + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION); +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 0cd5b02..d393015 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -28,7 +28,7 @@ # Parameters for the control interface. If this is specified, wpa_supplicant # will open a control interface that is available for external programs to # manage wpa_supplicant. The meaning of this string depends on which control -# interface mechanism is used. For all cases, the existance of this parameter +# interface mechanism is used. For all cases, the existence of this parameter # in configuration is used to determine whether the control interface is # enabled. # @@ -199,8 +199,12 @@ fast_reauth=1 # Config Methods # List of the supported configuration methods # Available methods: usba ethernet label display ext_nfc_token int_nfc_token -# nfc_interface push_button keypad +# nfc_interface push_button keypad virtual_display physical_display +# virtual_push_button physical_push_button +# For WSC 1.0: #config_methods=label display push_button keypad +# For WSC 2.0: +#config_methods=label virtual_display virtual_push_button keypad # Credential processing # 0 = process received credentials internally (default) @@ -224,6 +228,35 @@ fast_reauth=1 #filter_ssids=0 +# Interworking (IEEE 802.11u) + +# Enable Interworking +# interworking=1 + +# Homogenous ESS identifier +# If this is set, scans will be used to request response only from BSSes +# belonging to the specified Homogeneous ESS. This is used only if interworking +# is enabled. +# hessid=00:11:22:33:44:55 + +# Home Realm for Interworking +#home_realm=example.com + +# Username for Interworking network selection +#home_username=user + +# Password for Interworking network selection +#home_password=secret + +# CA certificate for Interworking network selection +#home_ca_cert=/etc/cert/ca.pem + +# IMSI in | | '-' | format +#home_imsi=232010000000000 + +# Milenage parameters for SIM/USIM simulator in :: format +#home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -350,7 +383,7 @@ fast_reauth=1 # # mixed_cell: This option can be used to configure whether so called mixed # cells, i.e., networks that use both plaintext and encryption in the same -# SSID, are allowed when selecting a BSS form scan results. +# SSID, are allowed when selecting a BSS from scan results. # 0 = disabled (default) # 1 = enabled # diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 6c36a1a..9698c68 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -17,6 +17,7 @@ #include "utils/list.h" #include "common/defs.h" +#include "config_ssid.h" extern const char *wpa_supplicant_version; extern const char *wpa_supplicant_license; @@ -34,6 +35,7 @@ struct ibss_rsn; struct scan_info; struct wpa_bss; struct wpa_scan_results; +struct hostapd_hw_modes; /* * Forward declarations of private structures used within the ctrl_iface @@ -180,6 +182,26 @@ struct wpa_params { * created. */ char *override_ctrl_interface; + + /** + * entropy_file - Optional entropy file + * + * This parameter can be used to configure wpa_supplicant to maintain + * its internal entropy store over restarts. + */ + char *entropy_file; +}; + +struct p2p_srv_bonjour { + struct dl_list list; + struct wpabuf *query; + struct wpabuf *resp; +}; + +struct p2p_srv_upnp { + struct dl_list list; + u8 version; + char *service; }; /** @@ -196,101 +218,21 @@ struct wpa_global { void **drv_priv; size_t drv_count; struct os_time suspend_time; + struct p2p_data *p2p; + struct wpa_supplicant *p2p_group_formation; + u8 p2p_dev_addr[ETH_ALEN]; + struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ + struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ + int p2p_disabled; + int cross_connection; }; -struct wpa_client_mlme { -#ifdef CONFIG_CLIENT_MLME - enum { - IEEE80211_DISABLED, IEEE80211_AUTHENTICATE, - IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED, - IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED - } state; - u8 prev_bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; - u16 aid; - u16 ap_capab, capab; - u8 *extra_ie; /* to be added to the end of AssocReq */ - size_t extra_ie_len; - u8 *extra_probe_ie; /* to be added to the end of ProbeReq */ - size_t extra_probe_ie_len; - enum wpa_key_mgmt key_mgmt; - - /* The last AssocReq/Resp IEs */ - u8 *assocreq_ies, *assocresp_ies; - size_t assocreq_ies_len, assocresp_ies_len; - - int auth_tries, assoc_tries; - - unsigned int ssid_set:1; - unsigned int bssid_set:1; - unsigned int prev_bssid_set:1; - unsigned int authenticated:1; - unsigned int associated:1; - unsigned int probereq_poll:1; - unsigned int use_protection:1; - unsigned int create_ibss:1; - unsigned int mixed_cell:1; - unsigned int wmm_enabled:1; - - struct os_time last_probe; - - unsigned int auth_algs; /* bitfield of allowed auth algs - * (WPA_AUTH_ALG_*) */ - int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ - int auth_transaction; - - struct os_time ibss_join_req; - u8 *probe_resp; /* ProbeResp template for IBSS */ - size_t probe_resp_len; - u32 supp_rates_bits; - - int wmm_last_param_set; - - int sta_scanning; - int scan_hw_mode_idx; - int scan_channel_idx; - enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; - struct os_time last_scan_completed; - int scan_oper_channel; - int scan_oper_freq; - int scan_oper_phymode; - u8 scan_ssid[32]; - size_t scan_ssid_len; - int scan_skip_11b; - int *scan_freqs; - - struct ieee80211_sta_bss *sta_bss_list; -#define STA_HASH_SIZE 256 -#define STA_HASH(sta) (sta[5]) - struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; - - int cts_protect_erp_frames; - - enum hostapd_hw_mode phymode; /* current mode */ - struct hostapd_hw_modes *modes; - size_t num_modes; - unsigned int hw_modes; /* bitfield of allowed hardware modes; - * (1 << HOSTAPD_MODE_*) */ - int num_curr_rates; - int *curr_rates; - int freq; /* The current frequency in MHz */ - int channel; /* The current IEEE 802.11 channel number */ - -#ifdef CONFIG_IEEE80211R - u8 current_md[6]; - u8 *ft_ies; - size_t ft_ies_len; -#endif /* CONFIG_IEEE80211R */ - - void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, - int freq); - void *public_action_cb_ctx; - -#else /* CONFIG_CLIENT_MLME */ - int dummy; /* to keep MSVC happy */ -#endif /* CONFIG_CLIENT_MLME */ +enum offchannel_send_action_result { + OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged + */, + OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ }; /** @@ -303,6 +245,7 @@ struct wpa_client_mlme { */ struct wpa_supplicant { struct wpa_global *global; + struct wpa_supplicant *parent; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; @@ -313,6 +256,7 @@ struct wpa_supplicant { #endif /* CONFIG_CTRL_IFACE_DBUS */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; + char *dbus_groupobj_path; #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ char bridge_ifname[16]; @@ -322,7 +266,7 @@ struct wpa_supplicant { os_time_t last_michael_mic_error; u8 bssid[ETH_ALEN]; u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this - * field contains the targer BSSID. */ + * field contains the target BSSID. */ int reassociate; /* reassociation requested */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ @@ -335,6 +279,7 @@ struct wpa_supplicant { int pairwise_cipher; int group_cipher; int key_mgmt; + int wpa_proto; int mgmt_group_cipher; void *drv_priv; /* private data used by driver_ops */ @@ -348,6 +293,12 @@ struct wpa_supplicant { */ #define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1) + struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */ + int sched_scan_timeout; + int sched_scan_interval; + int first_sched_scan; + int sched_scan_timed_out; + void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); struct dl_list bss; /* struct wpa_bss::list */ @@ -366,6 +317,7 @@ struct wpa_supplicant { enum wpa_states wpa_state; int scanning; + int sched_scanning; int new_connection; int reassociated_connection; @@ -383,11 +335,16 @@ struct wpa_supplicant { int scan_req; /* manual scan request; this forces a scan even if there * are no enabled networks in the configuration */ int scan_runs; /* number of scan runs since WPS was started */ + int *next_scan_freqs; + int scan_interval; /* time in sec between scans to find suitable AP */ - struct wpa_client_mlme mlme; unsigned int drv_flags; int max_scan_ssids; + int max_sched_scan_ssids; + int sched_scan_supported; + unsigned int max_match_sets; unsigned int max_remain_on_chan; + unsigned int max_stations; int pending_mic_error_report; int pending_mic_error_pairwise; @@ -404,12 +361,17 @@ struct wpa_supplicant { struct ibss_rsn *ibss_rsn; + int set_sta_uapsd; + int sta_uapsd; + int set_ap_uapsd; + int ap_uapsd; + #ifdef CONFIG_SME struct { u8 ssid[32]; size_t ssid_len; int freq; - u8 assoc_req_ie[80]; + u8 assoc_req_ie[200]; size_t assoc_req_ie_len; int mfp; int ft_used; @@ -419,6 +381,15 @@ struct wpa_supplicant { u8 prev_bssid[ETH_ALEN]; int prev_bssid_set; int auth_alg; + int proto; + + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending + * SA Query transaction identifiers */ + struct os_time sa_query_start; } sme; #endif /* CONFIG_SME */ @@ -429,14 +400,116 @@ struct wpa_supplicant { void *ap_configured_cb_data; #endif /* CONFIG_AP */ + unsigned int off_channel_freq; + struct wpabuf *pending_action_tx; + u8 pending_action_src[ETH_ALEN]; + u8 pending_action_dst[ETH_ALEN]; + u8 pending_action_bssid[ETH_ALEN]; + unsigned int pending_action_freq; + int pending_action_no_cck; + int pending_action_without_roc; + void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result); + unsigned int roc_waiting_drv_freq; + int action_tx_wait_time; + +#ifdef CONFIG_P2P + struct p2p_go_neg_results *go_params; + int create_p2p_iface; + u8 pending_interface_addr[ETH_ALEN]; + char pending_interface_name[100]; + int pending_interface_type; + int p2p_group_idx; + unsigned int pending_listen_freq; + unsigned int pending_listen_duration; + enum { + NOT_P2P_GROUP_INTERFACE, + P2P_GROUP_INTERFACE_PENDING, + P2P_GROUP_INTERFACE_GO, + P2P_GROUP_INTERFACE_CLIENT + } p2p_group_interface; + struct p2p_group *p2p_group; + int p2p_long_listen; /* remaining time in long Listen state in ms */ + char p2p_pin[10]; + int p2p_wps_method; + u8 p2p_auth_invite[ETH_ALEN]; + int p2p_sd_over_ctrl_iface; + int p2p_in_provisioning; + int pending_invite_ssid_id; + int show_group_started; + u8 go_dev_addr[ETH_ALEN]; + int pending_pd_before_join; + u8 pending_join_iface_addr[ETH_ALEN]; + u8 pending_join_dev_addr[ETH_ALEN]; + int pending_join_wps_method; + int p2p_join_scan_count; + int force_long_sd; + + /* + * Whether cross connection is disallowed by the AP to which this + * interface is associated (only valid if there is an association). + */ + int cross_connect_disallowed; + + /* + * Whether this P2P group is configured to use cross connection (only + * valid if this is P2P GO interface). The actual cross connect packet + * forwarding may not be configured depending on the uplink status. + */ + int cross_connect_enabled; + + /* Whether cross connection forwarding is in use at the moment. */ + int cross_connect_in_use; + + /* + * Uplink interface name for cross connection + */ + char cross_connect_uplink[100]; + + enum { + P2P_GROUP_REMOVAL_UNKNOWN, + P2P_GROUP_REMOVAL_REQUESTED, + P2P_GROUP_REMOVAL_IDLE_TIMEOUT, + P2P_GROUP_REMOVAL_UNAVAILABLE + } removal_reason; + + unsigned int p2p_cb_on_scan_complete:1; +#endif /* CONFIG_P2P */ + struct wpa_ssid *bgscan_ssid; const struct bgscan_ops *bgscan; void *bgscan_priv; - int connect_without_scan; + struct wpa_ssid *connect_without_scan; int after_wps; unsigned int wps_freq; + int wps_fragment_size; + int auto_reconnect_disabled; + + /* Channel preferences for AP/P2P GO use */ + int best_24_freq; + int best_5_freq; + int best_overall_freq; + + struct gas_query *gas; + +#ifdef CONFIG_INTERWORKING + unsigned int fetch_anqp_in_progress:1; + unsigned int network_select:1; + unsigned int auto_select:1; +#endif /* CONFIG_INTERWORKING */ + unsigned int drv_capa_known; + + struct { + struct hostapd_hw_modes *modes; + u16 num_modes; + u16 flags; + } hw; }; @@ -446,6 +519,7 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_state_txt(enum wpa_states state); +int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s); int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s); int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, @@ -462,6 +536,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); @@ -476,9 +551,14 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan); +int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, + unsigned int expire_age); +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, + unsigned int expire_count); int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys); +void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); @@ -499,14 +579,30 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); enum wpa_key_mgmt key_mgmt2driver(int key_mgmt); enum wpa_cipher cipher_suite2driver(int cipher); +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s); +void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); +void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); -void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, - struct wpa_bss *selected, - struct wpa_ssid *ssid); +int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_ssid *ssid); +void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx); +void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx); /* eap_register.c */ int eap_register_methods(void); +/** + * Utility method to tell if a given network is a persistent group + * @ssid: Network object + * Returns: 1 if network is a persistent group, 0 otherwise + */ +static inline int network_is_persistent_group(struct wpa_ssid *ssid) +{ + return ((ssid->disabled == 2) || ssid->p2p_persistent_group); +} + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 4af0cd0..69b0cf8 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -24,7 +24,6 @@ #include "wpa_supplicant_i.h" #include "driver_i.h" #include "rsn_supp/pmksa_cache.h" -#include "mlme.h" #include "sme.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" @@ -32,6 +31,7 @@ #include "wps_supplicant.h" #include "bss.h" #include "scan.h" +#include "notify.h" #ifndef CONFIG_NO_CONFIG_BLOBS @@ -210,9 +210,8 @@ static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx, wpa_s->group_cipher = cipher; } return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, - unicast ? wpa_s->bssid : - (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, unicast, (u8 *) "", 0, key, keylen); + unicast ? wpa_s->bssid : NULL, + keyidx, unicast, NULL, 0, key, keylen); } @@ -255,14 +254,29 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, "handshake"); pmk_len = PMK_LEN; - res = eapol_sm_get_key(eapol, pmk, PMK_LEN); - if (res) { - /* - * EAP-LEAP is an exception from other EAP methods: it - * uses only 16-byte PMK. - */ - res = eapol_sm_get_key(eapol, pmk, 16); - pmk_len = 16; + if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) { +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; + wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for " + "driver-based 4-way hs and FT"); + res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN); + if (res == 0) { + os_memcpy(pmk, buf + PMK_LEN, PMK_LEN); + os_memset(buf, 0, sizeof(buf)); + } +#else /* CONFIG_IEEE80211R */ + res = -1; +#endif /* CONFIG_IEEE80211R */ + } else { + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } } if (res) { @@ -271,6 +285,9 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, return; } + wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way " + "handshake", pmk, pmk_len); + if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk, pmk_len)) { wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver"); @@ -420,10 +437,6 @@ static void * wpa_supplicant_get_network_ctx(void *wpa_s) static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) { - os_memcpy(bssid, wpa_s->bssid, ETH_ALEN); - return 0; - } return wpa_drv_get_bssid(wpa_s, bssid); } @@ -471,8 +484,6 @@ static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md, const u8 *ies, size_t ies_len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) return sme_update_ft_ies(wpa_s, md, ies, ies_len); return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len); @@ -484,9 +495,6 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action, const u8 *ies, size_t ies_len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - return ieee80211_sta_send_ft_action(wpa_s, action, target_ap, - ies, ies_len); return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len); } @@ -497,9 +505,6 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) struct wpa_driver_auth_params params; struct wpa_bss *bss; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - return -1; - bss = wpa_bss_get_bssid(wpa_s, target_ap); if (bss == NULL) return -1; @@ -518,13 +523,142 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) #endif /* CONFIG_NO_WPA */ +#ifdef CONFIG_TDLS + +static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, + int *tdls_ext_setup) +{ + struct wpa_supplicant *wpa_s = ctx; + + *tdls_supported = 0; + *tdls_ext_setup = 0; + + if (!wpa_s->drv_capa_known) + return -1; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) + *tdls_supported = 1; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP) + *tdls_ext_setup = 1; + + return 0; +} + + +static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, + size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token, + status_code, buf, len); +} + + +static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpa_drv_tdls_oper(wpa_s, oper, peer); +} + + +static int wpa_supplicant_tdls_peer_addset( + void *ctx, const u8 *peer, int add, u16 capability, + const u8 *supp_rates, size_t supp_rates_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct hostapd_sta_add_params params; + + params.addr = peer; + params.aid = 1; + params.capability = capability; + params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; + params.ht_capabilities = NULL; + params.listen_interval = 0; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.set = !add; + + return wpa_drv_sta_add(wpa_s, ¶ms); +} + +#endif /* CONFIG_TDLS */ + + +enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) +{ + if (os_strcmp(field, "IDENTITY") == 0) + return WPA_CTRL_REQ_EAP_IDENTITY; + else if (os_strcmp(field, "PASSWORD") == 0) + return WPA_CTRL_REQ_EAP_PASSWORD; + else if (os_strcmp(field, "NEW_PASSWORD") == 0) + return WPA_CTRL_REQ_EAP_NEW_PASSWORD; + else if (os_strcmp(field, "PIN") == 0) + return WPA_CTRL_REQ_EAP_PIN; + else if (os_strcmp(field, "OTP") == 0) + return WPA_CTRL_REQ_EAP_OTP; + else if (os_strcmp(field, "PASSPHRASE") == 0) + return WPA_CTRL_REQ_EAP_PASSPHRASE; + return WPA_CTRL_REQ_UNKNOWN; +} + + +const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, + const char *default_txt, + const char **txt) +{ + const char *ret = NULL; + + *txt = default_txt; + + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: + *txt = "Identity"; + ret = "IDENTITY"; + break; + case WPA_CTRL_REQ_EAP_PASSWORD: + *txt = "Password"; + ret = "PASSWORD"; + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: + *txt = "New Password"; + ret = "NEW_PASSWORD"; + break; + case WPA_CTRL_REQ_EAP_PIN: + *txt = "PIN"; + ret = "PIN"; + break; + case WPA_CTRL_REQ_EAP_OTP: + ret = "OTP"; + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: + *txt = "Private key passphrase"; + ret = "PASSPHRASE"; + break; + default: + break; + } + + /* txt needs to be something */ + if (*txt == NULL) { + wpa_printf(MSG_WARNING, "No message for request %d", field); + ret = NULL; + } + + return ret; +} + #ifdef IEEE8021X_EAPOL #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -static void wpa_supplicant_eap_param_needed(void *ctx, const char *field, - const char *txt) +static void wpa_supplicant_eap_param_needed(void *ctx, + enum wpa_ctrl_req_type field, + const char *default_txt) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; + const char *field_name, *txt = NULL; char *buf; size_t buflen; int len; @@ -532,13 +666,23 @@ static void wpa_supplicant_eap_param_needed(void *ctx, const char *field, if (ssid == NULL) return; + wpas_notify_network_request(wpa_s, ssid, field, default_txt); + + field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt, + &txt); + if (field_name == NULL) { + wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed", + field); + return; + } + buflen = 100 + os_strlen(txt) + ssid->ssid_len; buf = os_malloc(buflen); if (buf == NULL) return; len = os_snprintf(buf, buflen, WPA_CTRL_REQ "%s-%d:%s needed for SSID ", - field, ssid->id, txt); + field_name, ssid->id, txt); if (len < 0 || (size_t) len >= buflen) { os_free(buf); return; @@ -572,6 +716,16 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized) authorized ? "Authorized" : "Unauthorized"); wpa_drv_set_supp_port(wpa_s, authorized); } + + +static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert); +} #endif /* IEEE8021X_EAPOL */ @@ -602,6 +756,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->eap_param_needed = wpa_supplicant_eap_param_needed; ctx->port_cb = wpa_supplicant_port_cb; ctx->cb = wpa_supplicant_eapol_cb; + ctx->cert_cb = wpa_supplicant_cert_cb; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -616,6 +771,16 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) } +static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek, + const u8 *kck, + const u8 *replay_ctr) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr); +} + + int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_WPA @@ -651,6 +816,13 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) ctx->send_ft_action = wpa_supplicant_send_ft_action; ctx->mark_authenticated = wpa_supplicant_mark_authenticated; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TDLS + ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa; + ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt; + ctx->tdls_oper = wpa_supplicant_tdls_oper; + ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset; +#endif /* CONFIG_TDLS */ + ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { @@ -674,6 +846,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, conf.peerkey_enabled = ssid->peerkey; conf.allowed_pairwise_cipher = ssid->pairwise_cipher; #ifdef IEEE8021X_EAPOL + conf.proactive_key_caching = ssid->proactive_key_caching; conf.eap_workaround = ssid->eap_workaround; conf.eap_conf_ctx = &ssid->eap; #endif /* IEEE8021X_EAPOL */ diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h index b571e4d..78c1b3d 100644 --- a/wpa_supplicant/wpas_glue.h +++ b/wpa_supplicant/wpas_glue.h @@ -15,9 +15,17 @@ #ifndef WPAS_GLUE_H #define WPAS_GLUE_H +enum wpa_ctrl_req_type; + int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s); int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s); void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, + const char *default_txt, + const char **txt); + +enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field); + #endif /* WPAS_GLUE_H */ diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index ba94d33..b0dafe4 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -24,6 +24,7 @@ #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_peer/eap.h" +#include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "config.h" #include "wpa_supplicant_i.h" @@ -32,10 +33,15 @@ #include "blacklist.h" #include "bss.h" #include "scan.h" +#include "ap.h" +#include "p2p/p2p.h" +#include "p2p_supplicant.h" #include "wps_supplicant.h" +#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 +#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); @@ -65,15 +71,25 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) } eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + int disabled = wpa_s->current_ssid->disabled; + unsigned int freq = wpa_s->assoc_freq; wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " - "try to associate with the received credential"); + "try to associate with the received credential " + "(freq=%u)", freq); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + if (disabled) { + wpa_printf(MSG_DEBUG, "WPS: Current network is " + "disabled - wait for user to enable"); + return 1; + } wpa_s->after_wps = 5; - wpa_s->wps_freq = wpa_s->assoc_freq; + wpa_s->wps_freq = freq; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); return 1; @@ -113,6 +129,8 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, if (wpa_drv_get_capa(wpa_s, &capa)) return; /* Unknown what driver supports */ + if (ssid->ssid == NULL) + return; bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len); if (bss == NULL) { wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS " @@ -178,6 +196,9 @@ static int wpa_supplicant_wps_cred(void *ctx, struct wpa_ssid *ssid = wpa_s->current_ssid; u8 key_idx = 0; u16 auth_type; +#ifdef CONFIG_WPS_REG_DISABLE_OPEN + int registrar = 0; +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */ if ((wpa_s->conf->wps_cred_processing == 1 || wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) { @@ -231,6 +252,13 @@ static int wpa_supplicant_wps_cred(void *ctx, if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " "on the received credential"); +#ifdef CONFIG_WPS_REG_DISABLE_OPEN + if (ssid->eap.identity && + ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0) + registrar = 1; +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */ os_free(ssid->eap.identity); ssid->eap.identity = NULL; ssid->eap.identity_len = 0; @@ -238,6 +266,8 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->eap.phase1 = NULL; os_free(ssid->eap.eap_methods); ssid->eap.eap_methods = NULL; + if (!ssid->p2p_group) + ssid->temporary = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); @@ -304,6 +334,16 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_NONE; ssid->proto = 0; +#ifdef CONFIG_WPS_REG_DISABLE_OPEN + if (registrar) { + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK + "id=%d - Credentials for an open " + "network disabled by default - use " + "'select_network %d' to enable", + ssid->id, ssid->id); + ssid->disabled = 1; + } +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */ break; case WPS_AUTH_SHARED: ssid->auth_alg = WPA_AUTH_ALG_SHARED; @@ -341,6 +381,7 @@ static int wpa_supplicant_wps_cred(void *ctx, return -1; } ssid->psk_set = 1; + ssid->export_keys = 1; } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) { os_free(ssid->passphrase); ssid->passphrase = os_malloc(cred->key_len + 1); @@ -349,6 +390,7 @@ static int wpa_supplicant_wps_cred(void *ctx, os_memcpy(ssid->passphrase, cred->key, cred->key_len); ssid->passphrase[cred->key_len] = '\0'; wpa_config_update_psk(ssid); + ssid->export_keys = 1; } else { wpa_printf(MSG_ERROR, "WPS: Invalid Network Key " "length %lu", @@ -371,6 +413,15 @@ static int wpa_supplicant_wps_cred(void *ctx, } +#ifdef CONFIG_P2P +static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_notif_pbc_overlap(wpa_s); +} +#endif /* CONFIG_P2P */ + + static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, struct wps_event_m2d *m2d) { @@ -378,15 +429,59 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); wpas_notify_wps_event_m2d(wpa_s, m2d); +#ifdef CONFIG_P2P + if (wpa_s->parent && wpa_s->parent != wpa_s) { + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D + "dev_password_id=%d config_error=%d", + m2d->dev_password_id, m2d->config_error); + } + if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) { + /* + * Notify P2P from eloop timeout to avoid issues with the + * interface getting removed while processing a message. + */ + eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s, + NULL); + } +#endif /* CONFIG_P2P */ } +static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { + "No Error", /* WPS_EI_NO_ERROR */ + "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ + "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ +}; + static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { - wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg); + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(wpa_s, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + if (wpa_s->parent && wpa_s->parent != wpa_s) + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, + fail->error_indication, + wps_event_fail_reason[fail->error_indication]); + } else { + wpa_msg(wpa_s, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + if (wpa_s->parent && wpa_s->parent != wpa_s) + wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + "msg=%d config_error=%d", + fail->msg, fail->config_error); + } wpas_clear_wps(wpa_s); wpas_notify_wps_event_fail(wpa_s, fail); +#ifdef CONFIG_P2P + wpas_p2p_wps_failed(wpa_s, fail); +#endif /* CONFIG_P2P */ } @@ -395,6 +490,9 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); wpa_s->wps_success = 1; wpas_notify_wps_event_success(wpa_s); +#ifdef CONFIG_P2P + wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); +#endif /* CONFIG_P2P */ } @@ -468,6 +566,59 @@ static void wpa_supplicant_wps_event_er_enrollee_remove( } +static void wpa_supplicant_wps_event_er_ap_settings( + struct wpa_supplicant *wpa_s, + struct wps_event_er_ap_settings *ap_settings) +{ + char uuid_str[100]; + char key_str[65]; + const struct wps_credential *cred = ap_settings->cred; + + key_str[0] = '\0'; + if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { + if (cred->key_len >= 8 && cred->key_len <= 64) { + os_memcpy(key_str, cred->key, cred->key_len); + key_str[cred->key_len] = '\0'; + } + } + + uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str)); + /* Use wpa_msg_ctrl to avoid showing the key in debug log */ + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS + "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x " + "key=%s", + uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len), + cred->auth_type, cred->encr_type, key_str); +} + + +static void wpa_supplicant_wps_event_er_set_sel_reg( + struct wpa_supplicant *wpa_s, + struct wps_event_er_set_selected_registrar *ev) +{ + char uuid_str[100]; + + uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str)); + switch (ev->state) { + case WPS_ER_SET_SEL_REG_START: + wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=START sel_reg=%d dev_passwd_id=%u " + "sel_reg_config_methods=0x%x", + uuid_str, ev->sel_reg, ev->dev_passwd_id, + ev->sel_reg_config_methods); + break; + case WPS_ER_SET_SEL_REG_DONE: + wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=DONE", uuid_str); + break; + case WPS_ER_SET_SEL_REG_FAILED: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG + "uuid=%s state=FAILED", uuid_str); + break; + } +} + + static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, union wps_event_data *data) { @@ -483,6 +634,10 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, wpa_supplicant_wps_event_success(wpa_s); break; case WPS_EV_PWD_AUTH_FAIL: +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee) + wpa_supplicant_ap_pwd_auth_fail(wpa_s); +#endif /* CONFIG_AP */ break; case WPS_EV_PBC_OVERLAP: break; @@ -502,6 +657,16 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, wpa_supplicant_wps_event_er_enrollee_remove(wpa_s, &data->enrollee); break; + case WPS_EV_ER_AP_SETTINGS: + wpa_supplicant_wps_event_er_ap_settings(wpa_s, + &data->ap_settings); + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + wpa_supplicant_wps_event_er_set_sel_reg(wpa_s, + &data->set_sel_reg); + break; + case WPS_EV_AP_PIN_SUCCESS: + break; } } @@ -519,7 +684,9 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) static void wpas_clear_wps(struct wpa_supplicant *wpa_s) { int id; - struct wpa_ssid *ssid, *remove_ssid = NULL; + struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; + + prev_current = wpa_s->current_ssid; eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); @@ -538,6 +705,11 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) id = -1; ssid = ssid->next; if (id >= 0) { + if (prev_current == remove_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, + NULL); + } wpas_notify_network_removed(wpa_s, remove_ssid); wpa_config_remove_network(wpa_s->conf, id); } @@ -548,8 +720,8 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; - wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " - "out"); + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " + "out"); wpas_clear_wps(wpa_s); } @@ -564,6 +736,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, return NULL; wpas_notify_network_added(wpa_s, ssid); wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 || wpa_config_set(ssid, "eap", "WSC", 0) < 0 || wpa_config_set(ssid, "identity", registrar ? @@ -575,12 +748,20 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, } if (bssid) { +#ifndef CONFIG_P2P struct wpa_bss *bss; int count = 0; +#endif /* CONFIG_P2P */ os_memcpy(ssid->bssid, bssid, ETH_ALEN); ssid->bssid_set = 1; + /* + * Note: With P2P, the SSID may change at the time the WPS + * provisioning is started, so better not filter the AP based + * on the current SSID in the scan results. + */ +#ifndef CONFIG_P2P dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0) continue; @@ -604,6 +785,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, ssid->ssid = NULL; ssid->ssid_len = 0; } +#endif /* CONFIG_P2P */ } return ssid; @@ -615,13 +797,26 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; + if (wpa_s->current_ssid) + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + /* Mark all other networks disabled and trigger reassociation */ ssid = wpa_s->conf->ssid; while (ssid) { int was_disabled = ssid->disabled; - ssid->disabled = ssid != selected; - if (was_disabled != ssid->disabled) - wpas_notify_network_enabled_changed(wpa_s, ssid); + /* + * In case the network object corresponds to a persistent group + * then do not send out network disabled signal. In addition, + * do not change disabled status of persistent network objects + * from 2 to 1 should we connect to another network. + */ + if (was_disabled != 2) { + ssid->disabled = ssid != selected; + if (was_disabled != ssid->disabled) + wpas_notify_network_enabled_changed(wpa_s, + ssid); + } ssid = ssid->next; } wpa_s->disconnected = 0; @@ -633,14 +828,31 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, } -int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + int p2p_group) { struct wpa_ssid *ssid; wpas_clear_wps(wpa_s); ssid = wpas_wps_add_network(wpa_s, 0, bssid); if (ssid == NULL) return -1; + ssid->temporary = 1; + ssid->p2p_group = p2p_group; +#ifdef CONFIG_P2P + if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { + ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); + if (ssid->ssid) { + ssid->ssid_len = wpa_s->go_params->ssid_len; + os_memcpy(ssid->ssid, wpa_s->go_params->ssid, + ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " + "SSID", ssid->ssid, ssid->ssid_len); + } + } +#endif /* CONFIG_P2P */ wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + if (wpa_s->wps_fragment_size) + ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid); @@ -649,7 +861,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid) int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin) + const char *pin, int p2p_group, u16 dev_pw_id) { struct wpa_ssid *ssid; char val[128]; @@ -659,13 +871,31 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid = wpas_wps_add_network(wpa_s, 0, bssid); if (ssid == NULL) return -1; + ssid->temporary = 1; + ssid->p2p_group = p2p_group; +#ifdef CONFIG_P2P + if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { + ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); + if (ssid->ssid) { + ssid->ssid_len = wpa_s->go_params->ssid_len; + os_memcpy(ssid->ssid, wpa_s->go_params->ssid, + ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " + "SSID", ssid->ssid, ssid->ssid_len); + } + } +#endif /* CONFIG_P2P */ if (pin) - os_snprintf(val, sizeof(val), "\"pin=%s\"", pin); + os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"", + pin, dev_pw_id); else { rpin = wps_generate_pin(); - os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin); + os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"", + rpin, dev_pw_id); } wpa_config_set(ssid, "phase1", val, 0); + if (wpa_s->wps_fragment_size) + ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid); @@ -673,6 +903,32 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, } +/* Cancel the wps pbc/pin requests */ +int wpas_wps_cancel(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode"); + return wpa_supplicant_ap_wps_cancel(wpa_s); + } +#endif /* CONFIG_AP */ + + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); + wpa_supplicant_cancel_scan(wpa_s); + wpas_clear_wps(wpa_s); + } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " + "deauthenticate"); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + wpas_clear_wps(wpa_s); + } + + return 0; +} + + #ifdef CONFIG_WPS_OOB int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, char *path, char *method, char *name) @@ -715,7 +971,8 @@ int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && wpas_wps_start_pin(wpa_s, NULL, - wpabuf_head(wps->oob_conf.dev_password)) < 0) + wpabuf_head(wps->oob_conf.dev_password), 0, + DEV_PW_DEFAULT) < 0) return -1; return 0; @@ -737,6 +994,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid = wpas_wps_add_network(wpa_s, 1, bssid); if (ssid == NULL) return -1; + ssid->temporary = 1; pos = val; end = pos + sizeof(val); res = os_snprintf(pos, end - pos, "\"pin=%s", pin); @@ -756,6 +1014,8 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, if (res < 0 || res >= end - pos) return -1; wpa_config_set(ssid, "phase1", val, 0); + if (wpa_s->wps_fragment_size) + ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid); @@ -805,16 +1065,71 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, if (wpa_s->wps_er == NULL) return; + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d " + "dev_password_id=%u sel_reg_config_methods=0x%x", + sel_reg, dev_passwd_id, sel_reg_config_methods); wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id, sel_reg_config_methods); #endif /* CONFIG_WPS_ER */ } +static u16 wps_fix_config_methods(u16 config_methods) +{ +#ifdef CONFIG_WPS2 + if ((config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | + WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { + wpa_printf(MSG_INFO, "WPS: Converting display to " + "virtual_display for WPS 2.0 compliance"); + config_methods |= WPS_CONFIG_VIRT_DISPLAY; + } + if ((config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS: Converting push_button to " + "virtual_push_button for WPS 2.0 compliance"); + config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ + + return config_methods; +} + + +static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, + struct wps_context *wps) +{ + wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname); + if (is_nil_uuid(wpa_s->conf->uuid)) { + struct wpa_supplicant *first; + first = wpa_s->global->ifaces; + while (first && first->next) + first = first->next; + if (first && first != wpa_s) { + os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid, + WPS_UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first " + "interface", wps->uuid, WPS_UUID_LEN); + } else { + uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " + "address", wps->uuid, WPS_UUID_LEN); + } + } else { + os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration", + wps->uuid, WPS_UUID_LEN); + } +} + + int wpas_wps_init(struct wpa_supplicant *wpa_s) { struct wps_context *wps; struct wps_registrar_config rcfg; + struct hostapd_hw_modes *modes; + u16 m; wps = os_zalloc(sizeof(*wps)); if (wps == NULL) @@ -831,22 +1146,42 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->dev.serial_number = wpa_s->conf->serial_number; wps->config_methods = wps_config_methods_str2bin(wpa_s->conf->config_methods); - if (wpa_s->conf->device_type && - wps_dev_type_str2bin(wpa_s->conf->device_type, - wps->dev.pri_dev_type) < 0) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { + wpa_printf(MSG_ERROR, "WPS: Both Label and Display config " + "methods are not allowed at the same time"); os_free(wps); return -1; } + wps->config_methods = wps_fix_config_methods(wps->config_methods); + wps->dev.config_methods = wps->config_methods; + os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, + WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types); + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); - wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */ + modes = wpa_s->hw.modes; + if (modes) { + for (m = 0; m < wpa_s->hw.num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B || + modes[m].mode == HOSTAPD_MODE_IEEE80211G) + wps->dev.rf_bands |= WPS_RF_24GHZ; + else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A) + wps->dev.rf_bands |= WPS_RF_50GHZ; + } + } + if (wps->dev.rf_bands == 0) { + /* + * Default to claiming support for both bands if the driver + * does not provide support for fetching supported bands. + */ + wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; + } os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN); - if (is_nil_uuid(wpa_s->conf->uuid)) { - uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", - wps->uuid, WPS_UUID_LEN); - } else - os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); + wpas_wps_set_uuid(wpa_s, wps); wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; @@ -929,12 +1264,13 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, } /* - * Start with WPS APs that advertise active PIN Registrar and - * allow any WPS AP after third scan since some APs do not set - * Selected Registrar attribute properly when using external - * Registrar. + * Start with WPS APs that advertise our address as an + * authorized MAC (v2.0) or active PIN Registrar (v1.0) and + * allow any WPS AP after couple of scans since some APs do not + * set Selected Registrar attribute properly when using + * external Registrar. */ - if (!wps_is_selected_pin_registrar(wps_ie)) { + if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { wpa_printf(MSG_DEBUG, " skip - WPS AP " "without active PIN Registrar"); @@ -944,7 +1280,7 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, " selected based on WPS IE"); } else { wpa_printf(MSG_DEBUG, " selected based on WPS IE " - "(Active PIN)"); + "(Authorized MAC or Active PIN)"); } wpabuf_free(wps_ie); return 1; @@ -976,7 +1312,7 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && - (wps_is_selected_pin_registrar(wps_ie) || + (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { /* allow wildcard SSID for WPS PIN */ ret = 1; @@ -989,6 +1325,28 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, ret = 1; } +#ifdef CONFIG_WPS_STRICT + if (wps_ie) { + if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len > + 0, bss->bssid) < 0) + ret = 0; + if (bss->beacon_ie_len) { + struct wpabuf *bcn_wps; + bcn_wps = wpa_scan_get_vendor_ie_multi_beacon( + bss, WPS_IE_VENDOR_TYPE); + if (bcn_wps == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " + "missing from AP Beacon"); + ret = 0; + } else { + if (wps_validate_beacon(wps_ie) < 0) + ret = 0; + wpabuf_free(bcn_wps); + } + } + } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_free(wps_ie); return ret; @@ -1006,12 +1364,21 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, if (!eap_is_wps_pbc_enrollee(&ssid->eap)) return 0; + wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is " + "present in scan results; selected BSSID " MACSTR, + MAC2STR(selected->bssid)); + /* Make sure that only one AP is in active PBC mode */ wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE); - if (wps_ie) + if (wps_ie) { sel_uuid = wps_get_uuid_e(wps_ie); - else + wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS", + sel_uuid, UUID_LEN); + } else { + wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include " + "WPS IE?!"); sel_uuid = NULL; + } dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { struct wpabuf *ie; @@ -1024,10 +1391,18 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, wpabuf_free(ie); continue; } + wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " + MACSTR, MAC2STR(bss->bssid)); uuid = wps_get_uuid_e(ie); + wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", + uuid, UUID_LEN); if (sel_uuid == NULL || uuid == NULL || - os_memcmp(sel_uuid, uuid, 16) != 0) { + os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ + wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " + MACSTR " and " MACSTR, + MAC2STR(selected->bssid), + MAC2STR(bss->bssid)); wpabuf_free(ie); break; } @@ -1046,6 +1421,7 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; + unsigned int pbc = 0, auth = 0, pin = 0, wps = 0; if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED) return; @@ -1056,17 +1432,24 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) if (!ie) continue; if (wps_is_selected_pbc_registrar(ie)) - wpa_msg_ctrl(wpa_s, MSG_INFO, - WPS_EVENT_AP_AVAILABLE_PBC); + pbc++; + else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0)) + auth++; else if (wps_is_selected_pin_registrar(ie)) - wpa_msg_ctrl(wpa_s, MSG_INFO, - WPS_EVENT_AP_AVAILABLE_PIN); + pin++; else - wpa_msg_ctrl(wpa_s, MSG_INFO, - WPS_EVENT_AP_AVAILABLE); + wps++; wpabuf_free(ie); - break; } + + if (pbc) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC); + else if (auth) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH); + else if (pin) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN); + else if (wps) + wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE); } @@ -1099,14 +1482,14 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf, } -int wpas_wps_er_start(struct wpa_supplicant *wpa_s) +int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) { #ifdef CONFIG_WPS_ER if (wpa_s->wps_er) { wps_er_refresh(wpa_s->wps_er); return 0; } - wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname); + wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter); if (wpa_s->wps_er == NULL) return -1; return 0; @@ -1127,8 +1510,8 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS_ER -int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, - const char *pin) +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin) { u8 u[UUID_LEN]; int any = 0; @@ -1137,7 +1520,8 @@ int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, any = 1; else if (uuid_str2bin(uuid, u)) return -1; - return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u, + return wps_registrar_add_pin(wpa_s->wps->registrar, addr, + any ? NULL : u, (const u8 *) pin, os_strlen(pin), 300); } @@ -1164,10 +1548,107 @@ int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, } +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id) +{ + u8 u[UUID_LEN]; + struct wpa_ssid *ssid; + struct wps_credential cred; + + if (uuid_str2bin(uuid, u)) + return -1; + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->ssid == NULL) + return -1; + + os_memset(&cred, 0, sizeof(cred)); + if (ssid->ssid_len > 32) + return -1; + os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len); + cred.ssid_len = ssid->ssid_len; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ? + WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK; + if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) + cred.encr_type = WPS_ENCR_AES; + else + cred.encr_type = WPS_ENCR_TKIP; + if (ssid->passphrase) { + cred.key_len = os_strlen(ssid->passphrase); + if (cred.key_len >= 64) + return -1; + os_memcpy(cred.key, ssid->passphrase, cred.key_len); + } else if (ssid->psk_set) { + cred.key_len = 32; + os_memcpy(cred.key, ssid->psk, 32); + } else + return -1; + } else { + cred.auth_type = WPS_AUTH_OPEN; + cred.encr_type = WPS_ENCR_NONE; + } + return wps_er_set_config(wpa_s->wps_er, u, &cred); +} + + +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin, struct wps_new_ap_settings *settings) +{ + u8 u[UUID_LEN]; + struct wps_credential cred; + size_t len; + + if (uuid_str2bin(uuid, u)) + return -1; + if (settings->ssid_hex == NULL || settings->auth == NULL || + settings->encr == NULL || settings->key_hex == NULL) + return -1; + + os_memset(&cred, 0, sizeof(cred)); + len = os_strlen(settings->ssid_hex); + if ((len & 1) || len > 2 * sizeof(cred.ssid) || + hexstr2bin(settings->ssid_hex, cred.ssid, len / 2)) + return -1; + cred.ssid_len = len / 2; + + len = os_strlen(settings->key_hex); + if ((len & 1) || len > 2 * sizeof(cred.key) || + hexstr2bin(settings->key_hex, cred.key, len / 2)) + return -1; + cred.key_len = len / 2; + + if (os_strcmp(settings->auth, "OPEN") == 0) + cred.auth_type = WPS_AUTH_OPEN; + else if (os_strcmp(settings->auth, "WPAPSK") == 0) + cred.auth_type = WPS_AUTH_WPAPSK; + else if (os_strcmp(settings->auth, "WPA2PSK") == 0) + cred.auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + if (os_strcmp(settings->encr, "NONE") == 0) + cred.encr_type = WPS_ENCR_NONE; + else if (os_strcmp(settings->encr, "WEP") == 0) + cred.encr_type = WPS_ENCR_WEP; + else if (os_strcmp(settings->encr, "TKIP") == 0) + cred.encr_type = WPS_ENCR_TKIP; + else if (os_strcmp(settings->encr, "CCMP") == 0) + cred.encr_type = WPS_ENCR_AES; + else + return -1; + + return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin, + os_strlen(pin), &cred); +} + + +static int callbacks_pending = 0; + static void wpas_wps_terminate_cb(void *ctx) { wpa_printf(MSG_DEBUG, "WPS ER: Terminated"); - eloop_terminate(); + if (--callbacks_pending <= 0) + eloop_terminate(); } #endif /* CONFIG_WPS_ER */ @@ -1176,6 +1657,7 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER if (wpa_s->wps_er) { + callbacks_pending++; wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s); wpa_s->wps_er = NULL; return 1; @@ -1183,3 +1665,65 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS_ER */ return 0; } + + +int wpas_wps_in_progress(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return 1; + } + + return 0; +} + + +void wpas_wps_update_config(struct wpa_supplicant *wpa_s) +{ + struct wps_context *wps = wpa_s->wps; + + if (wps == NULL) + return; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) { + wps->config_methods = wps_config_methods_str2bin( + wpa_s->conf->config_methods); + if ((wps->config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) == + (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) { + wpa_printf(MSG_ERROR, "WPS: Both Label and Display " + "config methods are not allowed at the " + "same time"); + wps->config_methods &= ~WPS_CONFIG_LABEL; + } + } + wps->config_methods = wps_fix_config_methods(wps->config_methods); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) { + wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, + wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION) + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID) + wpas_wps_set_uuid(wpa_s, wps); + + if (wpa_s->conf->changed_parameters & + (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) { + /* Update pointers to make sure they refer current values */ + wps->dev.device_name = wpa_s->conf->device_name; + wps->dev.manufacturer = wpa_s->conf->manufacturer; + wps->dev.model_name = wpa_s->conf->model_name; + wps->dev.model_number = wpa_s->conf->model_number; + wps->dev.serial_number = wpa_s->conf->serial_number; + } +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index ba2fb16..b38c091 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -35,9 +35,11 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s); void wpas_wps_deinit(struct wpa_supplicant *wpa_s); int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid); -int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, + int p2p_group); int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin); + const char *pin, int p2p_group, u16 dev_pw_id); +int wpas_wps_cancel(struct wpa_supplicant *wpa_s); int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, char *path, char *method, char *name); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, @@ -52,14 +54,20 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s); int wpas_wps_searching(struct wpa_supplicant *wpa_s); int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, char *end); -int wpas_wps_er_start(struct wpa_supplicant *wpa_s); +int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter); int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); -int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, - const char *pin); +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin); int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid); int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin); +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id); +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin, struct wps_new_ap_settings *settings); int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s); +int wpas_wps_in_progress(struct wpa_supplicant *wpa_s); +void wpas_wps_update_config(struct wpa_supplicant *wpa_s); #else /* CONFIG_WPS */